感觉自己DP确实太弱。。。
Codeforces Round #156 (Div. 1) A. Almost Arithmetical Progression
题意:在保持循序的情况下,从一个数组取出最多的数,这些数是交替出现的,例如,1,2,1,2,、。。
dp[i][j] 表示前i 个数,第 j 个数和第 i 个数被取出可以组成最长序列
我们用pre[i]记录 i 上一次出现的位置,这样就可以好好转移了。。。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 1000010 #define pi acos(-1.0) #define eps 1e-6 using namespace std; int dp[4001][4001],a[4010],pre[maxn]; int num[maxn] ; int main() { int i,m,n,k,j ; int id,ans; while(scanf("%d",&n) != EOF) { for( i = 1 ; i <= n ;i++) scanf("%d",&a[i]) ; memset(pre,-1,sizeof(pre)) ; memset(num,0,sizeof(num)) ; ans=1; for(i = 1 ; i <= n ;i++) { id=pre[a[i]]; num[a[i]]++; ans=max(num[a[i]],ans); pre[a[i]] = i ; for( j = 1 ; j < i ;j++) { if(id==-1)dp[j][i]=2 ; else if(j>id)dp[j][i] = 1+dp[id][j]; else if(j<id)dp[j][i] = dp[j][id]; ans=max(ans,dp[j][i]) ; } } cout <<ans<<endl; } return 0 ; } /256/problem/A
poj 1141 Brackets Sequence
题意: 给你一个字符串,你可以任意插入字符,使得塔变成回文串,长度要求最小,
然后把这个回文串输出来
dp[i][j]表示把 i-j变成回文串后的最短长度;
pre[x][y][2] 表示x,y从哪里转移过来的
然后 从 1,n经行 dfs,把需要增加字符和它匹配的位置标记
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 210 #define pi acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f using namespace std; int dp[maxn][maxn],pre[maxn][maxn][2] ; char a[maxn]; bool vi[maxn] ; bool check(char a,char b) { if(a=='('&&b==')') return true; if(a=='['&&b==']') return true; return false; } void out(char a) { if(a=='('||a==')')printf("()") ; else printf("[]"); } void dfs(int x,int y ) { if(x>y) return ; if(x>=y){vi[x]=true ;return;} int xx = pre[x][y][0],yy=pre[x][y][1] ; // cout << xx<<"--" <<yy<<endl; // cout<<x<<" "<<y<<endl; if(xx==x&&yy+1==y) { vi[y] = true; dfs(xx,yy) ; } else if(xx-1==x&&yy==y) { vi[x] = true; dfs(xx,yy); } else if(xx-1==x&&yy+1==y) { if(yy==xx)vi[xx]=1; dfs(xx,yy); } else { dfs(xx,yy) ; dfs(yy+1,y) ; } } int main() { int cnt1,cnt2,n,i; int j , k , T,m ; while(gets(a+1) > 0 ) { n = strlen(a+1) ; memset(dp,0,sizeof(dp)); memset(vi,0,sizeof(vi)); for( i = 1 ; i <= n ;i++) { dp[i][i]=2; for( j =i-1 ; j >= 1 ;j--) { dp[j][i] = dp[j][i-1]+2 ; pre[j][i][0]=j; pre[j][i][1]=i-1; if(dp[j+1][i]+2 < dp[j][i]){ dp[j][i] = dp[j+1][i]+2; pre[j][i][0]=j+1; pre[j][i][1]=i; } for( k = j+1 ; k < i ;k++) { if(dp[j][k]+dp[k+1][i] < dp[j][i]) { dp[j][i] = dp[j][k]+dp[k+1][i] ; pre[j][i][0]=j; pre[j][i][1]=k; } } if(check(a[j],a[i])&& 2+dp[j+1][i-1] < dp[j][i]) { dp[j][i] = dp[j+1][i-1]+2; pre[j][i][0]=j+1; pre[j][i][1]=i-1; } } } dfs(1,n); for( i = 1 ; i <= n ;i++) { if(vi[i])out(a[i]) ; else printf("%c",a[i]); } puts("") ; } return 0 ; }
poj 3280 Cheapest Palindrome
题意:每个字符都有删除它的代价增加它的代价,问最小的代价使得给出的字符串变成回文串
dp[i][j]表示,i-j变成字符串的最小代价
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 2010 #define pi acos(-1.0) #define eps 1e-6 using namespace std; int dp[maxn][maxn]; char a[maxn] ; int cost1[210] ,cost2[210]; int main() { int cnt1,cnt2,n,i; int j , k , T,m ; char s[5] ; while(scanf("%d%d",&n,&m) != EOF) { scanf("%s",a+1) ; for( i = 1 ; i <= n ;i++) { scanf("%s",s) ; scanf("%d%d",&cost1[s[0]],&cost2[s[0]]) ; } memset(dp,0x3f3f3f,sizeof(dp)) ; for( i = 1 ; i <= m;i++) { dp[i][i]=0; for( j = i-1 ; j >= 1 ;j--) { dp[j][i] = dp[j][i-1]+min(cost1[a[i]],cost2[a[i]]) ; dp[j][i] = min(dp[j][i],dp[j+1][i]+min(cost1[a[j]],cost2[a[j]])) ; // cout <<j <<" " << i << " "<<dp[j][i] << endl; if(a[i]==a[j]) { dp[j][i] = min(dp[j][i],dp[j+1][i-1]) ; if(i==j+1)dp[j][i]=0; } } } cout <<dp[1][m] << endl; } return 0 ; }
poj 2955 Brackets
题意:给出一个字符串,按先后循序不变选出一些字符组成一个回文串,要求这个回文串最长
dp[i][j]表示i-j可以选出最长的回文串
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 110 #define pi acos(-1.0) #define eps 1e-6 using namespace std; int dp[maxn][maxn]; char a[maxn] ; bool check(char a,char b) { if(a=='('&&b==')') return true; if(a=='['&&b==']') return true; return false; } int main() { int cnt1,cnt2,n,i; int j , k , T ; while(scanf("%s",a+1) != EOF) { if(a[1]=='e') break ; n = strlen(a+1) ; memset(dp,0,sizeof(dp)) ; for( j = 1 ; j <= n ;j++) { for( i = j-1; i >= 1 ;i--) { dp[i][j] = 0; if(check(a[i],a[j])) dp[i][j] = max(dp[i][j],dp[i+1][j-1]+1) ; for(k = i ; k < j ; k++) dp[i][j] = max(dp[i][k]+dp[k+1][j],dp[i][j]); } } cout << dp[1][n]*2 << endl; } return 0 ; }
hdu 2476 String painter
题意:每个你可以把a的一段居间刷成任意的一个字符,求把a变成 b 需要的最少刷的次数
dp[i][j] 表示把空串(相当于任意串)变成 b 中 i-j 子串的最小刷次数,
dp[i][j] = 1+dp[i+1][j] ;
如果 b[k]==b[i] ( k > i && k <= j ) 那么 b[i] 和 b[k] 可以一起刷出来 ,
dp[i][j] = min(dp[i][j],dp[i+1][k]+dp[k+1][j]) ;
然后 a和b 的匹配, ans[i]表示把 1-i变成相同最小的改变次数
如果 a[i]==b[i] 那么ans[i]=ans[i-1];
不然,就去枚举 1-i
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 210 #define pi acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f using namespace std; int dp[maxn][maxn],ans[maxn] ; char a[maxn],b[maxn] ; int main() { int cnt1,cnt2,n,i; int j , k , T,m ; while(scanf("%s%s",a+1,b+1) != EOF) { n = strlen(a+1) ; memset(dp,0,sizeof(dp)) ; for( i = 1 ; i <= n ;i++) { dp[i][i]=1; for( j = i-1 ; j >= 1 ;j--) { dp[j][i] = 1+dp[j+1][i]; for( k = j+1 ; k <= i ;k++)if(b[j]==b[k]) dp[j][i] = min(dp[j][i],dp[j+1][k]+dp[k+1][i]) ; // cout << j <<" " <<i<<" "<<dp[j][i]<<endl; } } ans[0]=0; for( i= 1 ; i <= n ;i++) { if(a[i]==b[i])ans[i]=ans[i-1] ; else { ans[i]=ans[i-1]+1; for( j = i ; j >= 1 ;j--) ans[i]=min(ans[i],ans[j-1]+dp[j][i]) ; } } cout <<ans[n]<<endl; } return 0 ; }
poj 3661 Running
题意:给出某个人n 分钟内每分钟可以跑步的距离,跑一分钟疲劳增加1 ,疲劳度不能超过 m ,
他某个时候可以选择休息,休息一分钟可以疲劳度减1,不过等到疲劳度为 0 才可以继续跑步
在 第n 分钟结束时,他的疲劳度必须为 0 ,问最长可以跑多远
dp[i][j] 表示,第i 分钟,疲劳度为 j 时最远可以跑多远
如果选择跑步 dp[i][j] = dp[i-1][j-1]+a[i]
不过 dp[i-1][j]不能转移到它,因为必须休息到0才能跑,
如果转移了,那么下一次用到它的转移就会出错。
dp[i][0] = dp[i-1][0] ;
或者第 j 分钟 选择休息 dp[i][0] = max(dp[i][0],dp[j][i-j]) ;
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 10010 #define pi acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f using namespace std; int dp[10001][510]; int a[maxn]; int main() { int cnt1,cnt2,n,i; int j , k , T,m ; while(scanf("%d%d",&n,&m) != EOF) { for( i = 1 ; i <= n ;i++) scanf("%d",&a[i]) ; memset(dp,0,sizeof(dp)) ; for( i = 1 ; i <= n ;i++) { for( j = 1 ; j <= m ;j++) { dp[i][j]=dp[i-1][j-1]+a[i]; } dp[i][0]=max(dp[i-1][0],dp[i-1][1]) ; for(int k=1;k<=m;k++)if(i-k>=0) dp[i][0]=max(dp[i][0],dp[i-k][k]); } cout << dp[n][0] << endl; } return 0 ; }
hdu 4597 Play Game
题意,两个数列,两个人,到一个人做的时候,可以取第一或者第二个数列的首或尾的一个数
问先手取到的数和最大和多少
dp[i][j][u][v] 表示,先手遇到第一数列状态 i-j 第二个数列 u-v时,可以取到的最大值,
注意边界就好了
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 110 #define pi acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f using namespace std; int dp[23][23][23][23] ; int a[23],b[23],sum[23][23][23][23] ; int sum11[maxn],sum2[maxn] ; int dfs(int x,int y,int xx,int yy ) { if(x>y&&xx>yy) return 0 ; if(dp[x][y][xx][yy] != -1) return dp[x][y][xx][yy] ; int Max=-1 ,tt,sum1=0; if(x<=y) sum1=sum11[y]-sum11[x-1] ; if(xx<=yy) sum1 += sum2[yy]-sum2[xx-1]; if(x<=y){ tt = dfs(x+1,y,xx,yy) ; Max = sum1-tt ; tt = dfs(x,y-1,xx,yy) ; Max= max(Max,sum1-tt) ; } if(xx<=yy){ tt = dfs(x,y,xx+1,yy) ; Max = max(Max,sum1-tt) ; tt = dfs(x,y,xx,yy-1) ; Max = max(Max,sum1-tt) ; } // cout<<x<<" "<<y<<" "<<xx<<" "<<yy<<" "<<Max<<endl; dp[x][y][xx][yy]=Max; return Max ; } int main() { int n,m,j,i,k; int T,ans1,u,v; cin >>T ; while(T--) { scanf("%d",&n); memset(dp,-1,sizeof(dp)) ; sum11[0]=sum2[0]=0; for( i = 1 ; i <= n ;i++) { scanf("%d",&a[i]) ; sum11[i] = sum11[i-1]+a[i] ; } for( i = 1 ; i <= n ;i++) { scanf("%d",&b[i]) ; sum2[i] = sum2[i-1]+b[i] ; } k = dfs(1,n,1,n); printf("%d ",k) ; } return 0 ; }
Codeforces Round #146 (Div. 1) B. Let's Play Osu!
题意:给出一段字符中每个字符出现的概率,如果有连续的 i 个 O那么价值就算上 i ^ 2
对于第 i 个字符,如果 它是 O , 假设 它前面有 k 个字符,那么当前价值就是 (K+1)^2 = k ^ 2 + 2*k+1
增加的价值就是 2*k+1 ,算上概率就是 (2*k+1)*p[i]
我们用dp[i] 表示以i结尾的连续O的段数的期望,dp[i] = dp[i-1]*p[i]+p[i] ;
所以到第i 个,增加的价值就是 2*dp[i-1]*p[i]+p[i];
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define mod 1000000007 #define maxn 100010 #define pi acos(-1.0) #define eps 1e-6 #define INF 0x3f3f3f3f using namespace std; double dp[maxn] ,p[maxn] ; int main() { int m,j,i,k; int T,ans1,u,v; int n; while(~scanf("%d",&n)) { for( i = 1 ; i <= n ;i++) scanf("%lf",&p[i]); double ans=0; dp[0]=0; for( i = 1 ; i <= n ;i++) { dp[i] = dp[i-1]*p[i]+p[i] ; ans += dp[i-1]*2*p[i]+p[i] ; } printf("%.11lf ",ans); } return 0 ; }