codeforces的dp专题

Posted jackyan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces的dp专题相关的知识,希望对你有一定的参考价值。

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

 





以上是关于codeforces的dp专题的主要内容,如果未能解决你的问题,请参考以下文章

「专题训练」Hard problem(Codeforces Round #367 Div. 2 C)

CodeForces 1005D Polycarp and Div 3(思维贪心dp)

「kuangbin带你飞」专题十五 数位DP

CodeForces 55D Beautiful numbers(数位dp&&离散化)

UESTC 电子科大专题训练 DP-N

Codeforces Round #267 (Div. 2) C. George and Job