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

原文地址:https://www.cnblogs.com/HDUjackyan/p/8996136.html

时间: 2024-11-12 20:02:01

codeforces的dp专题的相关文章

DP专题

DP专题 1. 背包模型 2. 子序列模型 3. 递推DP 4. 区间DP 5. 树形DP 6. 状压DP 学习资料:位操作基础篇之位操作全面总结 如何快速取得一个二进制状态的所有子状态 7. 概率DP 学习资料:简说期望类问题的解法 等等.......

[dp专题]hdu 1160 FatMouse&#39;s Speed

FatMouse's Speed Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 10172    Accepted Submission(s): 4521Special Judge Problem Description FatMouse believes that the fatter a mouse is, the faster i

codeforces 449D DP+容斥

Jzzhu and Numbers Time Limit:2000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2014-07-20) Description Jzzhu have n non-negative integers a1, a2, ..., an. We will call a sequence o

数位dp 专题

数位dp 专题 先来模板: int dfs(int i,int s,bool e) ///枚举第i位,第i位前一位的数字为s,e表示前缀是否已达到上界.(如果未达到,则枚举0000...~9999..,反之枚举0000...~abcd...) { if(i==-1) return 1; if(!e&&~f[i][s]) return f[i][s];/// f记录的是位数为i,前一数字为s的0000..~9999...的符合条件的个数 int res=0; int u=e?num[i]:9

插头DP专题

建议入门的人先看cd琦的<基于连通性状态压缩的动态规划问题>.事半功倍. 插头DP其实是比较久以前听说的一个东西,当初是水了几道水题,最近打算温习一下,顺便看下能否入门之类. 插头DP建议先理解“插头”的概念.然后会HASH表(这个其实是很基础的东西,应该都会的).然后就是DP. 以及特殊题目的特殊处理. 好像一般是求N,M<=12的网格图的某种回路数或某种通路数的方案数. 大体上每个题说几句特殊处理,有问题请纠正....题目的顺序基本上难度递增 另外代码我都是用括号匹配的.因为感觉连通

dp专题练习

雾...学初三爷开个坑放一些平时写的dp吧 顺便开另外一篇放一些学过的各种dp dp总结:https://www.cnblogs.com/henry-1202/p/9194066.html 开坑先放15道题,后面慢慢补 目标50道题啦~~,目前15/50 1.合唱队形 题目链接 LIS模板题,这道题只要正着求一遍LIS,倒着求一遍LIS,然后求max即可,注意因为求了两次LIS,一定会有一个人是被计算了两次的,所以在求max的时候要记得-1 使用O(n2)做法即可 #include <cstdi

区间dp专题练习

区间dp专题练习 题意 1.Equal Sum Partitions ? 这嘛东西,\(n^2\)自己写去 \[\ \] \[\ \] 2.You Are the One 感觉自己智力被吊打 \(dp[i][j]\)表示 , 对于当前的一个空栈 , \(i\)到\(j\)这一段都出栈的最小花费 显然是长得一副区间(诡)dp(异)的样子 , 如何转移呢?(建议自己想想吧) 对于一个\(dp[i][j]\),因为这个\(i\)必须是最先入栈的 , 所以我们可以枚举它的出栈时间\(k\) , 那么总贡

决策单调性优化dp 专题练习

决策单调性优化dp 专题练习 优化方法总结 一.斜率优化 对于形如 \(dp[i]=dp[j]+(i-j)*(i-j)\)类型的转移方程,维护一个上凸包或者下凸包,找到切点快速求解 技法: 1.单调队列 : 在保证插入和查询的x坐标均具有单调性时可以使用 2.单调栈+二分:保证插入有单调性,不保证查询有单调性 3.分治+ 1 或 2:在每次分治时将\([l,mid]\)这段区间排序后插入,然后更新右区间\([mid+1,r]\)的答案 二.分治.单调队列维护有单调性的转移 (甚至还有分治套分治)

数位dp专题 (HDU 4352 3652 3709 4507 CodeForces 55D POJ 3252)

数位dp核心在于状态描述,因为阶段很简单. 一般都是求有多少个数,当然也有求平方的变态题. 因为比这个数小的范围定然是从左至右开始小的,那么什么样的前缀对后面子数有相同的结果? HDU 3652 题意:求能被13整除且含有13这样数的个数. 赤裸裸的两个条件,加上个pre标明下前缀,其实直接开状态也是一样的.整除这个条件可以用余数来表示.余数转移:(mod*10+i)%13 /* *********************************************** Author :bi