zoukankan      html  css  js  c++  java
  • HDU4632 Poj2955 括号匹配 整数划分 P1880 [NOI1995]石子合并 区间DP总结

      题意:给定一个字符串 输出回文子序列的个数    一个字符也算一个回文

    很明显的区间dp  就是要往区间小的压缩!

    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s)
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 1005
    #define inf 0x3f3f3f3f
    #define mod 10007
    int dp[N][N];
    
    int main()
    {
        int cas;
        cin>>cas;
        char s[1005];
        int kase=0;
        while(cas--)
        {
            CLR(dp,0);
            RS(s+1);
            int n=strlen(s+1);
            rep(i,1,n)
            dp[i][i]=1;//长度为一的区间肯定是一个回文
            
           rep(i,2,n)
           for(int j=i-1;j>=1;j--)//这样保证了 len从小到大!
           {
                 dp[j][i]=(dp[j+1][i]+dp[j][i-1]-dp[j+1][i-1]+mod)%mod;//容斥原理  反正就是要将dp往区间短的转移
               if(s[i]==s[j])
                  dp[j][i] = (dp[j][i]+dp[j+1][i-1]+1+mod)%mod;//如果两边相等  那么这两个数可与i+1 到j-1里所有回文序列组成一个回文序列 且它们自己也是一个回文序列所以要加一
           }
          printf("Case %d: %d
    ",++kase,dp[1][n]);
        }
    }
    View Code

    Poj2955 括号匹配(一)

    给出一个的只有'(',')','[',']'四种括号组成的字符串,求 最多 有多少个括号满足题目里所描述的完全匹配。

    状态转移方程:dp[i][j]表示第i~j个字符间的最大匹配字符数。

    if(s[i] 与 s[j]匹配) dp[i][j] = d[[i+1][j-1] +2;

    dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);

    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s)
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 1005
    #define inf 0x3f3f3f3f
    #define mod 10007
    int dp[N][N];
    
    int main()
    {
      char s[N];
      while(RS(s+1)==1)
      {
          CLR(dp,0);
          int n=strlen(s+1);
          rep(i,1,n-1)
         // if(s[i]=='('&&s[i+1]==')'||s[i]=='['&&s[i+1]==']')
         //   dp[i][i+1]=1;
         rep(len,1,n)
         for(int i=1,j=i+len-1;j<=n;i++,j=i+len-1)
         {
              if((s[i]=='('&&s[j]==')')||(s[i]=='['&&s[j]==']'))
                    dp[i][j] = dp[i+1][j-1]+2; //如果匹配,先更新
                    
                for(int k = i;k<j;k++)//区间合并
                {//k<j
                    dp[i][j] = max(dp[i][j],dp[i][k]+dp[k+1][j]);
                }
         }
       cout<<dp[1][n]<<endl;
      }
    }

    整数划分

    给出一个数n,要求在n的数位间插入(m-1)个乘号,将n分成了m段,求这m段的最大乘积。

    样例输入

    2

    111 2

    1111 2

    样例输出

    11

    121

    状态转移方程为

    dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i])

    其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。

    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s)
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 1005
    #define inf 0x3f3f3f3f
    #define mod 10007
    int dp[N][N];
    int num[ N][N];
    int main()
    {
       char s[1000];
       int n,m;
       RS(s+1);
       RI(m);
       n=strlen(s+1);
       rep(i,1,n)
       {
           num[i][i]=s[i]-'0';
           rep(j,i+1,n)
           num[i][j]=num[i][j-1]*10+s[j]-'0';
       }
       rep(i,1,n)
       dp[i][0]=num[1][i];
    
       rep(j,1,m-1)//乘号一个个放入 因为每次dp[i][j] 由dp[i][j-1]转移而来  所以要j从小到大开始枚举  就像之前的要len从小到大开始枚举 因为每一个长区间取决于短区间
       rep(i,j+1,n)
       rep(k,j,i-1)
       dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i] );
      cout<<dp[n][m-1];
    }
    View Code

    石子合并

    题目描述

    在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

    试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

    输入输出格式

    输入格式:

    数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

    输出格式:

    输出共2行,第1行为最小得分,第2行为最大得分.

    输入输出样例

    输入样例#1: 复制
    4
    4 5 9 4
    输出样例#1: 复制
    43
    54


    特意去学习了区间dp 发现是区间dp入门题:石子划分的加强版 (当然 这题也是入门题) 对比那题 只是把链变成了环

    可以得到区间dp的方程:
    f[i][j] = max(f[i][k] + f[k+1][j] + 合并付出的代价) 这里的代价是 i到j的所有石子合

    如果对dp顺序没有加以设计的话 很容易写出
    i=1 to 10,j=1 to 10,k=1 to 9.当i=1,j=5,k=3时,显然状态f[k+1][j]没有结果。 f[4][5]的结果为i=4 j=5时求出的
    所以区间dp对执行顺序是有考究的:
    枚举j-i(也就是len),并在j-i中枚举k。这样,就能保证地推的正确
    #include<bits/stdc++.h>
    using namespace std;
    //input
    #define rep(i,x,y) for(int i=(x);i<=(y);++i)
    #define RI(n) scanf("%d",&(n))
    #define RII(n,m) scanf("%d%d",&n,&m);
    #define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
    #define RS(s) scanf("%s",s)
    #define LL long long
    #define REP(i,N)  for(int i=0;i<(N);i++)
    #define CLR(A,v)  memset(A,v,sizeof A)
    //////////////////////////////////
    #define N 300
    #define inf 0x3f3f3f3f
    int a[N];
    int dp1[N][N];//max
    int dp2[N][N];//min
    int s[N];
    int main()
    {
        int n;
        RI(n);
        rep(i,1,n)
        RI(a[i]),a[i+n]=a[i];
    
        rep(i,1,2*n)
        s[i]=s[i-1]+a[i];
        rep(len,1,n-1)
        {
            for(int i=1,j=i+len;i<=2*n&&j<=2*n;i++,j=i+len  )
                {
                    dp2[i][j]=inf;//注意求最小值的初始化
                    for(int k=i;k<j;k++)
                    {
                        dp1[i][j]=max(dp1[i][j],dp1[i][k]+dp1[k+1][j]+s[j]-s[i-1] );
                        dp2[i][j]=min(dp2[i][j],dp2[i][k]+dp2[k+1][j]+s[j]-s[i-1] );
                    }
                }
        }
        int ans1=0;
        int ans2=inf;
        rep(i,1,n)
        {
            ans1=max(ans1,dp1[i][i+n-1]);//注意减一
            ans2=min(ans2,dp2[i][i+n-1]);
        }
        cout<<ans2<<endl<<ans1;
        return 0;
    }
    View Code
  • 相关阅读:
    (OK) MIMP
    (OK) MIMP
    (OK) MIMP
    Linux内核报文收发-L3
    【Linux4.1.12源码分析】邻居子系统实现分析
    (OK) MIMP
    (OK) MIMP
    (OK) MIMP
    (OK) MIMP
    (OK) MIMP
  • 原文地址:https://www.cnblogs.com/bxd123/p/10513541.html
Copyright © 2011-2022 走看看