首页 > 代码库 > 最近做的DP类题的总结~

最近做的DP类题的总结~

感觉自己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-6using 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
View Code

 

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 0x3f3f3f3fusing 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 ;}
View Code

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-6using 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 ;}
View Code

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-6using 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 ;}
View Code

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 0x3f3f3f3fusing 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 ;}
View Code

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 0x3f3f3f3fusing 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 ;}
View Code

 

最近做的DP类题的总结~