例题:以下例题部分的内容来自https://blog.csdn.net/my_sunshine26/article/details/77141398

一、石子合并问题

1.(NYOJ737)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737

分析:我们dp[i][j]来表示合并第i堆到第j堆石子的最小代价。那么状态转移方程为dp[i][j]=min(dp[i][k]+dp[k+1][j]+w[i][j])  (s[i][j-1]<=k<=s[i+1][j])

其中w[i][j]表示把两部分合并起来的代价,即从第i堆到第j堆石子个数的和,为了方便查询,我们可以用sum[i]表示从第1堆到第i堆的石子个数和,那么w[i][j]=sum[j]-sum[i-1].

用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
const ll inf=1e18;
ll dp[maxn][maxn];
ll sum[maxn],a[maxn];
int s[maxn][maxn]; int main()
{
int n,i,j,k,x,y,z,len;
while ( scanf("%d",&n)!=EOF )
{
for ( i=;i<=n;i++ )
{
for ( j=;j<=n;j++ ) dp[i][j]=inf;
dp[i][i]=;
s[i][i]=i;
}
sum[]=;
for ( i=;i<=n;i++ )
{
scanf("%lld",&a[i]);
sum[i]=a[i]+sum[i-];
}
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=len+i-;
if ( j>n ) break;
for ( k=s[i][j-];k<=s[i+][j];k++ )
{
if ( dp[i][j]>dp[i][k]+dp[k+][j]+sum[j]-sum[i-] )
{
dp[i][j]=dp[i][k]+dp[k+][j]+sum[j]-sum[i-];
s[i][j]=k;
}
}
}
}
printf("%lld\n",dp[][n]);
}
return ;
}

NYOJ737

2.(HDOJ3506)http://acm.hdu.edu.cn/showproblem.php?pid=3506

题意:上一题的升级版,将上一层的线性变成一个圈。这时候我们只需要将N变成n=2*N-1即可,最后ans=min(dp[i][i+n-1])

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
const ll inf=1e18;
ll dp[maxn][maxn];
ll sum[maxn],a[maxn];
int s[maxn][maxn]; int main()
{
int n,i,j,k,x,y,z,len,N;
ll ans;
while ( scanf("%d",&N)!=EOF )
{
n=*N-;
for ( i=;i<=n;i++ )
{
for ( j=;j<=n;j++ ) dp[i][j]=inf;
dp[i][i]=;
s[i][i]=i;
}
sum[]=;
for ( i=;i<=N;i++ )
{
scanf("%lld",&a[i]);
sum[i]=a[i]+sum[i-];
}
for ( i=;i<N;i++ ) sum[i+N]=a[i]+sum[i+N-];
for ( len=;len<=N;len++ )
{
for ( i=;i<=n;i++ )
{
j=len+i-;
if ( j>n ) break;
for ( k=s[i][j-];k<=s[i+][j];k++ )
{
if ( dp[i][j]>dp[i][k]+dp[k+][j]+sum[j]-sum[i-] )
{
dp[i][j]=dp[i][k]+dp[k+][j]+sum[j]-sum[i-];
s[i][j]=k;
}
}
}
}
ans=inf;
for ( i=;i<=N;i++ )
{
j=i+N-;
ans=min(ans,dp[i][j]);
}
printf("%lld\n",ans);
}
return ;
}

HDOJ3506

二、括号匹配问题

1.(POJ2955)http://poj.org/problem?id=2955

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

分析:用dp[i][j]表示区间[i,j]里最大完全匹配数。只要得到了dp[i][j],那么就可以得到dp[i-1][j+1]  dp[i-1][j+1]=dp[i][j]+(s[i-1]于s[j+1]匹配?2:0).

然后利用状态转移方程更新一下区间最优解即可。dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j])

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
char s[maxn];
ll dp[maxn][maxn]; int main()
{
int n,i,j,k,x,y,z,len;
while ( scanf("%s",s+)!=EOF && s[]!='e' )
{
n=strlen(s+);
memset(dp,,sizeof(dp));
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
if ( (s[i]=='('&&s[j]==')') || (s[i]=='['&&s[j]==']') ) dp[i][j]=dp[i+][j-]+;
for ( k=i;k<j;k++ ) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+][j]);
}
}
printf("%lld\n",dp[][n]);
}
return ;
}

POJ2955

2.(NYOJ15)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=15

分析:最少添加的括号数=总括号-最大匹配的括号数,代码于上一题基本一致

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
char s[maxn];
ll dp[maxn][maxn]; int main()
{
int n,i,j,k,x,y,z,len,T;
scanf("%d",&T);
while ( T-- )
{
scanf("%s",s+);
n=strlen(s+);
memset(dp,,sizeof(dp));
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
if ( (s[i]=='('&&s[j]==')') || (s[i]=='['&&s[j]==']') ) dp[i][j]=dp[i+][j-]+;
for ( k=i;k<j;k++ ) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+][j]);
}
}
printf("%lld\n",n-dp[][n]);
}
return ;
}

NYOJ15

三、整数划分问题

1.(NYOJ746)http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=746

分析:用dp[i][j]表示从第一位到第i位共插入j个乘号后乘积的最大值。根据区间DP的思想我们可以从插入较少乘号的结果算出插入较多乘号的结果。

方法是当我们要放第j的乘号时枚举放的位置。状态转移方程为dp[i][j]=max(dp[i][j],dp[k][j-1]*num[k+1][i])。其中num[i][j]表示从s[i]到s[j]这段连续区间代表的数值。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
ll dp[maxn][maxn];
ll num[maxn][maxn]; int main()
{
int T,n,m,i,j,k,x,y,z;
char s[maxn];
scanf("%d",&T);
while ( T-- )
{
scanf("%s%d",s+,&m);
n=strlen(s+);
memset(dp,,sizeof(dp));
for ( i=;i<=n;i++ )
{
num[i][i]=s[i]-'';
for ( j=i+;j<=n;j++ ) num[i][j]=num[i][j-]*+s[j]-'';
}
for ( i=;i<=n;i++ ) dp[i][]=num[][i];
for ( j=;j<m;j++ )
{
for ( i=;i<=n;i++ )
{
for ( k=;k<i;k++ ) dp[i][j]=max(dp[i][j],dp[k][j-]*num[k+][i]);
}
}
printf("%lld\n",dp[n][m-]);
}
return ;
}

NYOJ746

习题:

1.(HDOJ4632)http://acm.hdu.edu.cn/showproblem.php?pid=4632

题意:给定一个字符串,求这个字符串中包含多少回文子串(子串可以不连续)

分析;dp[i][j]表示从第i个字符到第j个字符中包含的回文子串的个数。初始化时dp[i][i]=1(因为自己本身也算做一个回文串),其他dp[i][j]=0

dp[i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+mod)%mod (容斥思想)

if ( s[i]==s[j] ) dp[i][j]+=dp[i+1][j-1]+1

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
const int mod=;
char s[maxn];
int dp[maxn][maxn]; int main()
{
int T,i,j,k,h,n,m,ans,len;
scanf("%d",&T);
for ( h=;h<=T;h++ )
{
scanf("%s",s+);
n=strlen(s+);
memset(dp,,sizeof(dp));
for ( i=;i<=n;i++ ) dp[i][i]=;
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
dp[i][j]=(dp[i][j-]+dp[i+][j]-dp[i+][j-]+mod)%mod;
if ( s[i]==s[j] ) dp[i][j]=(dp[i][j]+dp[i+][j-]++mod)%mod;
}
}
printf("Case %d: %d\n",h,dp[][n]%mod);
}
return ;
}

HDOJ4632

2.(HDOJ4745)http://acm.hdu.edu.cn/showproblem.php?pid=4745

题意:求最长非连续回文串

分析:先将环变成链,dp[i][j]表示区间(i,j)范围内最长的非连续回文串的长度,转移时dp[i][j]=max(max(dp[i+1][j],dp[i][j-1]),x) 当s[i]==s[j]时x=dp[i+1][j-1]+2,否则x=dp[i+1][j-1]

最后的答案在dp[i][i+N-1]中和dp[i][N-2]+1(共起点的情况)中去寻找

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
int a[maxn];
int dp[maxn][maxn]; int main()
{
int T,i,j,k,N,n,m,x,y,z,ans,len;
while ( scanf("%d",&N)!=EOF && N )
{ n=*N-;
for ( i=;i<=N;i++ ) scanf("%d",&a[i]);
for ( i=;i<N;i++ ) a[i+N]=a[i];
memset(dp,,sizeof(dp));
for ( i=;i<=n;i++ ) dp[i][i]=;
ans=;
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
int x=dp[i+][j-];
if ( a[i]==a[j] ) x+=;
dp[i][j]=max(max(dp[i+][j],dp[i][j-]),x);
}
}
for ( i=;i<=N;i++ ) ans=max(ans,dp[i][i+N-]);
for ( i=;i<=N+;i++ ) ans=max(ans,dp[i][i+N-]+);
printf("%d\n",ans);
}
return ;
}

HDOJ4745

3.(HDOJ2476)http://acm.hdu.edu.cn/showproblem.php?pid=2476

推荐此博客:https://blog.csdn.net/a601025382s/article/details/12379565

题意:给定两个字符串a和b,求最少需要对a进行多少次操作,才能将a变成b。每次操作时将a中任意一段变成任意一个字母所组成的段。

题解:动态规划题。dp[i][j]表示a中i到j段变成b需要的最少次数。递推公式:dp[i][j]=min(dp[i][k]+dp[k+1][j])(i<=k<j)。接着就是判断分界点了,对于字符串b,只有将相同字符一起刷才能减少操作数。所以每次碰到b[i]==b[k]时,可以减少一次操作,因为刷一次[i,k]再刷[i+1,k-1]和分别刷[i,i][k,k],[i+,k,k+1]是一样的,可操作数会减少。

注意:由于如果一段子串两端相等,会成端更新,从而改变中间子串的字符,所以处理时可假定所以a中单个字符都需要一次变化才能变成b。之后动态规划完成后再处理a和b中形同位置相同字符的情况。

另一种理解方式:不考虑起始串,将起始串默认为空串,找出所有dp值(dp[i][j]表示i到j这段空子串转换成目标串需要的最小次数)后,再通过ans[i]来求得最小变换值。ans[i]表示前i+1长度的子串转换成目标串需要的最小次数。

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=;
const int inf=1e9;
int dp[maxn][maxn],ans[maxn];
char s1[maxn],s2[maxn]; int main()
{
int i,j,k,x,y,z,n,len;
while ( scanf("%s%s",s1+,s2+)!=EOF )
{
n=strlen(s1+);
memset(dp,,sizeof(dp));
for ( i=;i<=n;i++ ) dp[i][i]=;
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
dp[i][j]=dp[i+][j]+;
for ( k=i+;k<=j;k++ )
{
if ( s2[i]==s2[k] ) dp[i][j]=min(dp[i][j],dp[i+][k]+dp[k+][j]);
}
}
}
for ( i=;i<=n;i++ ) ans[i]=dp[][i];
for ( i=;i<=n;i++ )
{
if ( s1[i]==s2[i] )
{
if ( i== ) ans[i]=;
else ans[i]=ans[i-];
}
else
{
for ( k=;k<=i;k++ ) ans[i]=min(ans[i],ans[k]+dp[k+][i]);
}
}
printf("%d\n",ans[n]);
}
return ;
}

HDOJ2476

4.(HDOJ5115)http://acm.hdu.edu.cn/showproblem.php?pid=5115

题意:有一排狼,每只狼有一个伤害A,还有一个伤害B。杀死一只狼的时候,会受到这只狼的伤害A和这只狼两边的狼的伤害B的和。如果某位置的狼被杀,那么杀它左边的狼时就会收到来自右边狼的B,因为这两只狼是相邻的了。求杀掉一排狼的最小代价。

分析:因为杀死一只狼所受的伤害总数是固定所以单独记录即可,只用考虑受到旁边狼攻击的伤害。dp[i][j]表示杀死区间区内[i,j]内的狼所需要的最小代价.

对于区间[i,j]我们只需要枚举出哪匹狼是需要最后杀死的即可。dp[i][j]=min(dp[i][k-1]+dp[k+1][j]+a[i-1]+a[j+1]) (i<k<j,边界单独考虑)

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
const ll inf=1e15;
ll dp[maxn][maxn];
int a[maxn]; int main()
{
int T,n,m,i,j,k,h,x,y,z,len;
ll sum;
scanf("%d",&T);
for ( h=;h<=T;h++ )
{
scanf("%d",&n);
sum=;
for ( i=;i<=n;i++ )
{
scanf("%d",&x);
sum+=x;
}
for ( i=;i<=n;i++ ) scanf("%d",&a[i]);
a[]=a[n+]=;
for ( i=;i<=n;i++ )
{
for ( j=;j<=n;j++ ) dp[i][j]=inf;
dp[i][i]=a[i-]+a[i+];
}
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
x=a[i-]+a[j+];
dp[i][j]=min(dp[i+][j]+x,dp[i][j-]+x);
for ( k=i+;k<j;k++ ) dp[i][j]=min(dp[i][j],dp[i][k-]+dp[k+][j]+x);
}
}
sum+=dp[][n];
printf("Case #%d: %lld\n",h,sum);
}
return ;
}

HDOJ5115

5.(HDOJ4283)http://acm.hdu.edu.cn/showproblem.php?pid=4283

推荐此博客:https://www.cnblogs.com/kedebug/archive/2012/12/10/2811053.html

题意:

给定一个序列,序列内的人有屌丝值Di,第i个人如果是第k个出场,那么他的屌丝值为Di * (k-1),  但是导演可以通过一个栈来调整序列里面人的出场顺序。

求一个出场序列使总屌丝值最小。

思路:导演对于这个出场顺序的影响只是一定程度上的。比如说:

1. 第一个人第k个出场

2. 那么要求2~k的人都要在第一个人前面出场

3. k+1~n的人都要在k以后出场

明白了上面的过程,就可以定义区间dp[i, j]表示区间[i, j]在相对于i为起点情况下i在第k个出场的最小屌丝总值。

1. dp[i, j] = (k - i) * Di + dp[i+1, k]

2. dp[i, j] += (k + 1 - i) * (sum[j] - sum[k]) + dp[k+1, j]

 #include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=;
const ll inf=1e18;
ll dp[maxn][maxn],sum[maxn];
int a[maxn]; int main()
{
int T,i,j,k,h,x,y,z,ans,len,n;
scanf("%d",&T);
for ( h=;h<=T;h++ )
{
scanf("%d",&n);
sum[]=;
for ( i=;i<=n;i++ )
{
scanf("%d",&a[i]);
sum[i]=a[i]+sum[i-];
}
for ( i=;i<=n;i++ )
{
for ( j=i;j<=n;j++ ) dp[i][j]=inf;
dp[i][i]=;
}
for ( len=;len<=n;len++ )
{
for ( i=;i<=n;i++ )
{
j=i+len-;
if ( j>n ) break;
dp[i][j]=min(dp[i+][j]+sum[j]-sum[i],dp[i+][j]+(j-i)*a[i]);
for ( k=i+;k<j;k++ )
dp[i][j]=min(dp[i][j],dp[i+][k]+a[i]*(k-i)+dp[k+][j]+(sum[j]-sum[k])*(k-i+));
}
}
printf("Case #%d: %lld\n",h,dp[][n]);
}
return ;
}

HDOJ4283

小结:区间DP过程大致相同,大都满足第一层循环枚举长度,第二层循环枚举起点。最内层往往有两种形式,第一种是需要在[i,j]中找一个分割点k使得将[i,j]分成[i,k]和[k+1,j]这样两个区间能够得到最优解

第二种形式是[i,j]可以由[i,j-1]或者[i,j+1]转移过来.重要的是找出新添加的元素(可以是k或者i)与之前那个len-1长度区间的关系

专题训练之区间DP的更多相关文章

  1. 2019年9月训练(贰)区间DP (luogu 4290)

    区间DP luogu 4290 明显的区间DP. 定义 dp[l][r][k]/*表示区间[l,r]能否凑成k(W,I,N,G)字符*/mp['W']=1;mp['I']=2;mp['N']=3;mp ...

  2. 专题训练之数位DP

    推荐以下一篇博客:https://blog.csdn.net/wust_zzwh/article/details/52100392 1.(HDOJ2089)http://acm.hdu.edu.cn/ ...

  3. dp专题训练

    ****************************************************************************************** 动态规划 专题训练 ...

  4. DP专题训练之HDU 2955 Robberies

    打算专题训练下DP,做一道帖一道吧~~现在的代码风格完全变了~~大概是懒了.所以.将就着看吧~哈哈 Description The aspiring Roy the Robber has seen a ...

  5. kuangbin专题十二 POJ3186 Treats for the Cows (区间dp)

    Treats for the Cows Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7949   Accepted: 42 ...

  6. 「kuangbin带你飞」专题二十二 区间DP

    layout: post title: 「kuangbin带你飞」专题二十二 区间DP author: "luowentaoaa" catalog: true tags: - ku ...

  7. 区间dp专题练习

    区间dp专题练习 题意 1.Equal Sum Partitions ? 这嘛东西,\(n^2\)自己写去 \[\ \] \[\ \] 2.You Are the One 感觉自己智力被吊打 \(dp ...

  8. [kuangbin带你飞]专题二十二 区间DP

            ID Origin Title   17 / 60 Problem A ZOJ 3537 Cake   54 / 105 Problem B LightOJ 1422 Hallowee ...

  9. dp暑假专题 训练记录

    A 回文串的最小划分 题意:给出长度不超过1000的字符串,把它分割成若干个回文字串,求能分成的最少字串数. #include <iostream> #include <cstdio ...

随机推荐

  1. 设计模式C++实现(1)——策略(Strategy)模式

    目录 策略模式 应用案例 实现的关键 Talk is cheap,let's See The Code 设计思想 参考 策略模式 策略模式定义了一系列算法和行为(也就是策略),他们可以在运行时相互替换 ...

  2. Oracle集合

    --union 并集 select * from emp where ename like '%A%' union select * from emp where ename like '%M%'; ...

  3. Codeforces Round #553 (Div. 2) C

    C. Problem for Nazar time limit per test 1 second memory limit per test 256 megabytes input standard ...

  4. Javascript中Generator(生成器)

    阅读目录 Generator的使用: yield yield* next()方法 next()方法的参数 throw方法() return()方法: Generator中的this和他的原型 实际使用 ...

  5. PHP autoload与spl_autoload自动加载机制的深入理解

    PHP autoload与spl_autoload自动加载机制的深入理解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-05我要评论 本篇文章是对PHP中的autoload与spl_ ...

  6. 王者荣耀交流协会-Alpha发布用户使用报告

    用户数量:10人 姓名如下(包括化名):张小斌.王瑞瑞.蛋蛋.小美.晨曦.小丽.张利刚.小闫.小谢.小崔 寻找的用户多为王者荣耀交流协会成员的同学,对管理时间有着强烈的需求,也对PSP Daily软件 ...

  7. 关于算法的时间复杂度O(f(n))

    (一)算法时间复杂度定义: 在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级.算法的时间复杂度,也就是算法的时间量度,记作:T(n ...

  8. 01慕课网《vue.js2.5入门》——基础知识

    前端框架 Vue.js2.5 2018-05-12 Vue官网:https://cn.vuejs.org/ 基础语法+案例实践+TodoList+Vue-cli构建工具+TodoList Vue基础语 ...

  9. python爬虫调用搜索引擎及图片爬取实战

    实战三-向搜索引擎提交搜索请求 关键点:利用搜索引擎提供的接口 百度的接口:wd="要搜索的内容" 360的接口:q="要搜索的内容" 所以我们只要把我们提交给 ...

  10. Sqlserver学习研究

    关注关键词 :Sqlserver实用工具配置步骤 1)创建实用工具控制点(UCP) 2)连接到现有UCP 3)相UCP注册SQL Server实例 4)创建数据层应用程序 5)设置资源运行状况策略 6 ...