zoukankan      html  css  js  c++  java
  • 最近做的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-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
    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 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 ;
    }
    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-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 ;
    }
    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-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 ;
    }
    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 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 ;
    }
    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 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 ;
    }
    View Code

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

     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 ;
    }
    View Code
  • 相关阅读:
    浏览网页的过程
    端口转发和端口映射
    代码审计入门之BlueCMS v1.6 sp1
    php伪协议总结
    phar反序列化
    iOS开发之GCD使用总结
    深入理解Android NDK日志符号化
    Android 开源项目源码解析之DynamicLoadApk 源码解析
    Gilt如何将微服务部署到AWS环境,介绍ION-Roller
    100分程序员的8个习惯
  • 原文地址:https://www.cnblogs.com/20120125llcai/p/4024773.html
Copyright © 2011-2022 走看看