1.(467C)http://codeforces.com/problemset/problem/467/C
题意:有一个长为n的序列,选取k个长度为m的子序列(子序列中不能有位置重复),求所取的k个序列求和的最大值是多少
分析:设dp[i][j]在[j,n]范围内取了i个子序列求和所得的最大值,用sum[i]表示[1,i]的求和。转移方程为dp[i][j]=max(dp[i-1][j+m]+sum[j+m-1]-sum[j-1],dp[i][j+1]),表示要不要选择[j,j+m-1]这段为其中一个子序列
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const ll maxn=5050; 7 ll dp[maxn][maxn],a[maxn],sum[maxn]; 8 9 int main() 10 { 11 ll n,m,k,i,j,x,y,z,ans; 12 while ( scanf("%lld%lld%lld",&n,&m,&k)!=EOF ) { 13 for ( i=1;i<=n;i++ ) scanf("%lld",&a[i]); 14 sum[0]=0; 15 for ( i=1;i<=n;i++ ) sum[i]=sum[i-1]+a[i]; 16 memset(dp,0,sizeof(dp)); 17 ans=0; 18 for ( i=1;i<=k;i++ ) { 19 dp[i][n-i*m+1]=sum[n]-sum[n-i*m]; 20 for ( j=n-i*m;j>0;j-- ) { 21 dp[i][j]=max(dp[i-1][j+m]+sum[j+m-1]-sum[j-1],dp[i][j+1]); 22 } 23 } 24 for ( i=1;i<=n-k*m+1;i++ ) ans=max(ans,dp[k][i]); 25 printf("%lld\n",ans); 26 } 27 return 0; 28 }
467C
2.(706C)http://codeforces.com/problemset/problem/706/C
题意:有n个字符串,每个字符串反转需要a[i]的费用,先求花费最少使得对于任意第i个字符串的字典序都大于等于第i-1个字符串,若不存在输出-1
分析:设dp[i][j],j=0表示第i个字符串不反转,j=1表示第i个字符串反转,dp[i][j]表示前i个字符串都已经按顺序排好的最小花费(初始化除第一项外都为inf)。
转移方程有4条:
if ( s[i]>=s[i-1] ) dp[i][0]=min(dp[i][0],dp[i-1][0]);
if ( s[i]>=s_[i-1] ) dp[i][0]=min(dp[i][0],dp[i-1][1]);
if ( s_[i]>=s[i-1] ) dp[i][1]=min(dp[i][1],dp[i-1][0]+a[i]);
if ( s_[i]>=s_[i-1] ) dp[i][1]=min(dp[i][1],dp[i-1][1]+a[i]);
注意:审题!操作是整体反转,字典序的顺序是大于等于(不要遗漏等于)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<string> 5 #include<iostream> 6 using namespace std; 7 typedef long long ll; 8 const ll maxn=1e5+10; 9 const ll inf=1e15; 10 ll dp[maxn][2],a[maxn]; 11 string s[maxn],s_[maxn]; 12 13 int main() 14 { 15 ll n,m,i,j,k,x,y,z,ans; 16 while ( scanf("%lld",&n)!=EOF ) { 17 for ( i=1;i<=n;i++ ) scanf("%d",&a[i]); 18 for ( i=1;i<=n;i++ ) { 19 cin>>s[i]; 20 s_[i]=s[i]; 21 reverse(s_[i].begin(),s_[i].end()); 22 } 23 //for ( i=1;i<=n;i++ ) cout<<s[i]<<" "<<s_[i]<<endl; 24 for ( i=1;i<=n;i++ ) dp[i][0]=dp[i][1]=inf; 25 dp[1][0]=0; 26 dp[1][1]=a[1]; 27 for ( i=2;i<=n;i++ ) { 28 if ( s[i]>=s[i-1] ) dp[i][0]=min(dp[i][0],dp[i-1][0]); 29 if ( s[i]>=s_[i-1] ) dp[i][0]=min(dp[i][0],dp[i-1][1]); 30 if ( s_[i]>=s[i-1] ) dp[i][1]=min(dp[i][1],dp[i-1][0]+a[i]); 31 if ( s_[i]>=s_[i-1] ) dp[i][1]=min(dp[i][1],dp[i-1][1]+a[i]); 32 } 33 ans=min(dp[n][0],dp[n][1]); 34 if ( ans==inf ) printf("-1\n"); 35 else printf("%lld\n",ans); 36 } 37 return 0; 38 }
706C
3.(611C)http://codeforces.com/problemset/problem/611/C
题意:有一个n*m的地图,地图上有“.”和“#”,当相邻的两块都为“.”时可以放置一个多米诺骨牌,现给定一个矩形区域,问在这个矩形区域内有多少种放置骨牌的方法
分析:因为有横着放和竖着放两种方式,所以我们将这两种方式分开来看。用row[i][j]表示对于第i行前j个方格多米诺骨牌横放有多少种可能。col[j][i]表示对于第j列前i个方格多米诺骨牌竖着放有多少可能。
最后对于左上角为(x1,y1)和右下角为(x2,y2)大小的矩阵来说,该方格中的方案数为:
for ( i=x1;i<=x2;i++ ) ans+=(row[i][y2]-row[i][y1]);
for ( i=y1;i<=y2;i++ ) ans+=(col[i][x2]-col[i][x1]);
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const ll maxn=510; 7 char s[maxn][maxn]; 8 ll mp[maxn][maxn],row[maxn][maxn],col[maxn][maxn]; 9 10 int main() 11 { 12 ll n,m,i,j,k,x,y,z,x1,x2,y1,y2,ans,q; 13 while ( scanf("%lld%lld",&n,&m)!=EOF ) { 14 for ( i=1;i<=n;i++ ) scanf("%s",s[i]+1); 15 memset(mp,0,sizeof(mp)); 16 memset(row,0,sizeof(row)); 17 memset(col,0,sizeof(col)); 18 for ( i=1;i<=n;i++ ) { 19 for ( j=1;j<=m;j++ ) { 20 if ( s[i][j]==‘.‘ ) mp[i][j]=1; 21 } 22 } 23 for ( i=1;i<=n;i++ ) { 24 for ( j=1;j<=m;j++ ) { 25 row[i][j]=row[i][j-1]; 26 if ( mp[i][j] && mp[i][j-1] ) row[i][j]++; 27 } 28 } 29 for ( j=1;j<=m;j++ ) { 30 for ( i=1;i<=n;i++ ) { 31 col[j][i]=col[j][i-1]; 32 if ( mp[i][j] && mp[i-1][j] ) col[j][i]++; 33 } 34 } 35 scanf("%lld",&q); 36 while ( q-- ) { 37 scanf("%lld%lld%lld%lld",&x1,&y1,&x2,&y2); 38 ans=0; 39 for ( i=x1;i<=x2;i++ ) ans+=(row[i][y2]-row[i][y1]); 40 for ( i=y1;i<=y2;i++ ) ans+=(col[i][x2]-col[i][x1]); 41 printf("%lld\n",ans); 42 } 43 } 44 return 0; 45 }
611C
原文地址:https://www.cnblogs.com/HDUjackyan/p/8996136.html