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\n",++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

 

转载于:https://www.cnblogs.com/bxd123/p/10513541.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值