前言:这两天没有写什么题目,把前两周做的有些意思的背包题和最长递增、公共子序列写了个总结。反过去写总结,总能让自己有一番收获......就区间dp来说,一开始我完全不明白它是怎么应用的,甚至于看解题报告都看不明白,而到了现在,遇到区间dp之类的题目,我不至于没有任何方向,慢慢的推导,有些题目没有自己想象的那么难,还是可以推导出转移方程的,有些题目,在自己推导过后,与解题报告相对照,也总能有一番全新的收获。我是觉得,解题报告需要看,但是怎么看,如何看,却是值得思量.......

1、Light oj 1422 Halloween Costumes

题意:给你n天需要穿的衣服的样式,每次可以套着穿衣服,脱掉的衣服就不能再用了(可以再穿),问至少要带多少条衣服才能参加所有宴会

http://lightoj.com/volume_showproblem.php?problem=1422

思路:首先dp[i][j]代表从区间i到区间j需要的最少穿衣服数量,我采取的是从下向上更新的。那么,面临第i件衣服,首先我们考虑穿上它,那么它所在的区间dp[i][j]=dp[i+1][j]+1;

接着考虑是否可以不用穿上它?在什么条件下,可以不用穿这件衣服呢?只有当区间i+1~~j里面已经穿过这件衣服的时候,就可以考虑不用再穿这件衣服。那么假设i+1<=k<=j

其中第k件衣服与第i件一样,那么第k件衣服穿上,第i件衣服不穿的情况的最少穿衣数==dp[i+1][k-1]+dp[k][j];第i件衣服不穿,那么就从第i+1件衣服开始计算,第k件衣服存在,那么在这个断点,我们该怎么判断是dp[i+1][k-1]+dp[k][j]呢?可以直接从数据推导出这个关系,也可以这样,在第k区间的时候,a[k]可能不是它自己本身穿的,而是在第k区间到第j区间,存在一个k<tmp<=j,有a[tmp]==a[k]......所以会是dp[i+1][k-1]+dp[k][j]........

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}
int dp[105][105],a[105];
int main()
{
int text,h=0;
scanf("%d",&text);
while(text--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
dp[i][i]=1;
for(int i=n-1;i>=1;i--)
{
for(int j=i+1;j<=n;j++)
{
dp[i][j]=dp[i+1][j]+1;
for(int k=i+1;k<=j;k++)
if(a[i]==a[k])
{
dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]);
}
}
}
printf("Case %d: %d\n",++h,dp[1][n]);
}
return 0;
}

2、 poj2955(括号匹配问题)

题意:给你一串()[]括号,要你求出这串括号的最大匹配个数,如'('与')'匹配,为2个,'['与']'匹配,为2个,其他不能匹配.......

思路:dp[i][j]代表从区间i到区间j所匹配的括号的最大个数,首先,假设不匹配,那么dp[i][j]=dp[i+1][j];然后查找i+1~~j有木有与第i个括号匹配的

有的话,dp[i][j]=max(dp[i][j],dp[i+1][k-1]+dp[k][j]+2).....

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std; int dp[105][105];
char a[105];
int max(int x,int y)
{
if(x>y)
return x;
else return y;
}
int main()
{
while(scanf("%s",a+1)>0&&a[1]!='e')
{
a[0]=2;
int len=strlen(a);
len--;
//printf("%d\n\n",len);
//for(int i=1;i<=len;i++)
//printf("%d %c\n",i,a[i]);
memset(dp,0,sizeof(dp));
for(int i=len-1;i>=1;i--)
{
for(int j=i+1;j<=len;j++)
{
dp[i][j]=dp[i+1][j];
for(int k=i+1;k<=j;k++)
{
if((a[i]=='('&&a[k]==')')||(a[i]=='['&&a[k]==']'))
{
dp[i][j]=max(dp[i][j],dp[i+1][k-1]+dp[k][j]+2);
//printf("%d %d %c %c\n",i,k,a[i],a[k]);
}
}
}
}
printf("%d\n",dp[1][len]);
}
return 0;
}

3、poj3280(推荐)

题意:给你m个字符,其中有n种字符,每种字符都有两个值,分别是增加一个这样的字符的代价,删除一个这样的字符的代价,让你求将原先给出的那串字符变成回文串的最小代价。

思路:dp[i][j]代表区间i到区间j成为回文串的最小代价,那么对于dp[i][j]有三种情况:

1、dp[i+1][j]表示区间i到区间j已经是回文串了的最小代价,那么对于s[i]这个字母,我们有两种操作,删除与添加,对应有两种代价,dp[i+1][j]+add[s[i]],dp[i+1][j]+del[s[i]],取这两种代价的最小值;

2、dp[i][j-1]表示区间i到区间j-1已经是回文串了的最小代价,那么对于s[j]这个字母,同样有两种操作,dp[i][j-1]+add[s[j]],dp[i][j-1]+del[s[j]],取最小值

3、若是s[i]==s[j],dp[i+1][j-1]表示区间i+1到区间j-1已经是回文串的最小代价,那么对于这种情况,我们考虑dp[i][j]与dp[i+1][j-1]的大小........

然后dp[i][j]取上面这些情况的最小值.........

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[2005][2005],add[27],del[27];
char s[2005];
int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)>0)
{
scanf("%s",s+1);
s[0]=2;
for(int i=1;i<=n;i++)
{
char ch[10];
int tmp1,tmp2;
scanf("%s%d%d",ch,&tmp1,&tmp2);
add[ch[0]-'a'+1]=tmp1;
del[ch[0]-'a'+1]=tmp2;
}
memset(dp,0,sizeof(dp));
for(int i=m-1;i>=1;i--)
{
for(int j=i+1;j<=m;j++)
{
dp[i][j]=min(dp[i+1][j]+add[s[i]-'a'+1],dp[i+1][j]+del[s[i]-'a'+1]);
int tmp=min(dp[i][j-1]+add[s[j]-'a'+1],dp[i][j-1]+del[s[j]-'a'+1]);
dp[i][j]=min(dp[i][j],tmp);
if(s[i]==s[j])
dp[i][j]=min(dp[i][j],dp[i+1][j-1]);
}
}
printf("%d\n",dp[1][m]);
}
return 0;
}

4、poj1141(区间dp记录路径问题)

题意:给出一串括号,要你补上最少的括号使这一串括号都匹配........

思路:dp[i][j]表示从区间i到区间j使其所以括号匹配需要补上的最少括号数,那么当出现一个括号时,首先考虑它不与后面匹配的情况,那么需要加一个相对应的括号,让之匹配dp[i][j]=dp[i+1][j]+1;

然后再考虑,若是后面有括号可以让它匹配的情况,那么假设i<k<=j,当s[i]=='('&&s[k]==')'的时候,考虑动态转移,dp[i][j]=dp[i+1][k-1]+dp[k][j]-1

为什么这个动态方程减1呢,因为我将与之匹配的那个括号重新计算了一次,当s[k]==')'的时候,在计算dp[k][k]的时候,我的状态转移已经把这个括号自动匹配了一次,所以要减去这次匹配的........

然后就是记录路径了,开一个二维数组a[i][j],当a[i][j]==-1的时候,表示dp[i][j]这个状态是从dp[i+1][j]推导过来的,当a[i][j]>0的时候,表示dp[i][j]是从dp[i+1][a[i][j]-1]以及dp[k][j]这两个状态推导过来的,那么注意到当a[i][j]!=-1的时候,就正好表示s[i]与s[a[i][j]]匹配,说明在第i个括号这个地方只需要输出它自己本身,其他的,若是a[i][j]==-1的,都需要输出它自身以及与它自身匹配的括号.........

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[300][300],a[300][300],b[300];
char s[300];
void print(int i,int j)
{
if(i>=j)
return;
if(a[i][j]==-1)
print(i+1,j);
if(a[i][j]>0)
{
b[i]=1;
b[a[i][j]]=1;
print(i+1,a[i][j]-1);
print(a[i][j],j);
}
}
int main()
{
while(gets(s+1)>0) //这里注意,有直接输入\n的情况.........
{
s[0]=2;
memset(dp,0,sizeof(dp));
memset(a,-1,sizeof(a));
memset(b,0,sizeof(b));
int len=strlen(s);
len--;
for(int i=1;i<=len;i++)
dp[i][i]=1;
for(int i=len-1;i>=1;i--)
{
for(int j=i+1;j<=len;j++)
{
dp[i][j]=dp[i+1][j]+1;
a[i][j]=-1;
for(int k=i+1;k<=j;k++)
if((s[i]=='('&&s[k]==')')||(s[i]=='['&&s[k]==']'))
{
if(dp[i][j]>dp[i+1][k-1]+dp[k][j]-1)
{
dp[i][j]=dp[i+1][k-1]+dp[k][j]-1;
a[i][j]=k;
}
}
}
}
print(1,len);
for(int i=1;i<=len;i++)
{
if(b[i]==1)
printf("%c",s[i]);
else
{
if(s[i]=='('||s[i]==')')
printf("()");
else
printf("[]");
}
}
printf("\n");
}
return 0;
}

5、poj1651(推荐)

题意:给你一组数字,第一个和最后一个数字不可以取出去,其它任意取出去,当你要取出一个数字时,它有一个代价,这个代价就是与它相邻的两个数的乘积,求除了首位两位数字,把其他数字都取出来,它们的代价之和的最小值........

思路:这题目,只有自己做过才能体会......我是说不出来......

Sample Input
6
10 1 50 50 20 5
Sample Output
3650

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[105][105],a[105];
int min(int x,int y)
{
if(x>y)
return y;
else
return x;
}
int main()
{
int n;
while(scanf("%d",&n)>0)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int i=n-2;i>=1;i--)
{
dp[i][i+2]=a[i]*a[i+1]*a[i+2];
for(int j=i+3;j<=n;j++)
{
dp[i][j]=a[i]*a[i+1]*a[j]+dp[i+1][j];
dp[i][j]=min(dp[i][j],a[i]*a[j-1]*a[j]+dp[i][j-1]);
for(int k=i+2;k<j-1;k++)
dp[i][j]=min(a[i]*a[k]*a[j]+dp[i][k]+dp[k][j],dp[i][j]);
}
}
printf("%d\n",dp[1][n]);
}
return 0;
}

6、poj3661(推荐)

题意:给你一个n,m,n表示有n分钟,每i分钟对应的是第i分钟能跑的距离,m代表最大疲劳度,每跑一分钟疲劳度+1,当疲劳度==m,必须休息,在任意时刻都可以选择休息,如果选择休息,那么必须休息到疲劳度为0,当然,当疲劳度为0的时候也是可以继续选择休息的,求在n分钟后疲劳度为0所跑的最大距离

思路:dp[i][j]表示在第i分钟疲劳度为j的时候所跑的最大距离,dp[i][j]=dp[i-1][j-1]+d[i];这个转移,说的是,第i分钟选择跑步,当然,第i分钟可以选择不跑步,那么就是选择休息,题目说了,选择休息的话必须要休息到疲劳度为0才可以跑,那还有一点,当疲劳度为0了,还是选择继续休息呢?dp[i][0]=dp[i-1][0];
选择休息,那么疲劳度到0了,这一点的最大距离怎么做呢?dp[i][0]=max(dp[i][0],dp[i-k][k])   (0<k<=m&&i-k>0)

这样所有的状态都考虑完了,可以写代码了.......

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[10005][505],a[10005];
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)>0)
{
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
dp[i][j]=dp[i-1][j-1]+a[i];
dp[i][0]=dp[i-1][0];
for(int k=1;k<=m;k++)
if(i-k>=0)
dp[i][0]=max(dp[i][0],dp[i-k][k]);
}
printf("%d\n",dp[n][0]);
}
return 0;
}

7、hdu2476(推荐)

给出两串字符,要你将第一串字符变成第二串字符,每次可以改变连续的一个或多个字符,求最少的修改次数

思路:还是区间dp问题,说起来也挺简单的,只是记录路径问题,有些难以想到..........

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int dp[105][105],a[105];
char s[105],t[105];
int main()
{
while(scanf("%s",s+1)>0)
{
scanf("%s",t+1);
s[0]=t[0]=2;
int len=strlen(s);
len--;
memset(dp,0,sizeof(dp));
memset(a,0,sizeof(a));
for(int i=1;i<=len;i++)
dp[i][i]=1;
for(int i=len-1;i>=1;i--)
{
for(int j=i+1;j<=len;j++)
{
//if(s[i]!=t[i])
dp[i][j]=dp[i+1][j]+1;
for(int k=i+1;k<=j;k++)
if(t[i]==t[k])
{
dp[i][j]=min(dp[i][j],dp[i+1][k-1]+dp[k][j]);
}
}
}
for(int i=1;i<=len;i++)
{
a[i]=dp[1][i];
if(s[i]==t[i])
{
if(i==1)
a[i]=0;
else
a[i]=a[i-1];
}
else
for(int j=1;j<i;j++)
a[i]=min(a[i],a[j]+dp[j+1][i]); }
printf("%d\n",a[len]); //printf("%d\n",dp[1][len]);
}
return 0;
}

8、zoj3537(最优三角划分)

9、编辑距离问题

题目描述:

要求两字符串有差异的字符个数。例如: 
aaaaabaaaaa 
aaaaacaabaa 
这两个字符串,最大公共字串长度是5,但它们只有两个字符不同,函数输出值应为2。 
如果是: 
aaabbbcccddd 
aaaeeeddd 
函数的输出值应该是6。

比较形象地形容一下,把两个字符串排成上下两行,每个字符串都可以在任何位置插入空格以便上下对齐,每个列上至少有一个字符来自这两个字符串。当对齐程度最高的时候,没有对上的列的数即为函数输出值。 
aaabbbcccddd 
aaaeeeddd 
最优对齐状态是: 
aaabbbcccddd 
aaaeee     ddd 
没有对上的列是6,函数输出值为6。 
如果是: 
abcde 
acefg 
最优对齐状态是: 
abcde 
a  c  efg 
没有对上的列数是4,函数输出值为4。

问题抽象归类:(编辑距离问题)

设A和B是2个字符串。要用最少的字符操作将字符串A转换为字符串B。这里所说的字符操作包括:

(1)删除一个字符;
(2)插入一个字符;
(3)将一个字符改为另一个字符。
将字符串A变换为字符串B所用的最少字符操作数称为字符串A到B的编辑距离,记为d(A,B)。试设计一个有效算法,对任给的2个字符串A和B,计算出它们的编辑距离d(A,B)。
要求:
输入:第1行是字符串A,第2行是字符串B。
输出:字符串A和B的编辑距离d(A,B)

思路:动态规划

开一个二维数组d[i][j]来记录a0-ai与b0-bj之间的编辑距离,要递推时,需要考虑对其中一个字符串的删除操作、插入操作和替换操作分别花费的开销,从中找出一个最小的开销即为所求

具体算法:

首先给定第一行和第一列,然后,每个值d[i,j]这样计算:d[i][j]   =   min(d[i-1][j]+1,d[i][j-1]+1,d[i-1][j-1]+(s1[i]  ==  s2[j]?0:1));   
 最后一行,最后一列的那个值就是最小编辑距离

#include <stdio.h>
#include <string.h>
char s1[1000],s2[1000];
int min(int a,int b,int c) {
int t = a < b ? a : b;
return t < c ? t : c;
}
void editDistance(int len1,int len2)
{
int** d=new int*[len1+1];
for(int k=0;k<=len1;k++)
d[k]=new int[len2+1];
int i,j;
for(i = 0;i <= len1;i++)
d[i][0] = i;
for(j = 0;j <= len2;j++)
d[0][j] = j;
for(i = 1;i <= len1;i++)
for(j = 1;j <= len2;j++)
{
int cost = s1[i] == s2[j] ? 0 : 1;
int deletion = d[i-1][j] + 1;
int insertion = d[i][j-1] + 1;
int substitution = d[i-1][j-1] + cost;
d[i][j] = min(deletion,insertion,substitution);
}
printf("%d\n",d[len1][len2]);
for(int k=0;i<=len1;k++)
delete[] d[k];
delete[] d;
}
int main()
{
while(scanf("%s %s",s1,s2) != EOF)
editDistance(strlen(s1),strlen(s2));
}

区间dp总结篇的更多相关文章

  1. 区间dp之四边形不等式优化详解及证明

    看了那么久的四边形不等式优化的原理,今天终于要写一篇关于它的证明了. 在平时的做题中,我们会遇到这样的区间dp问题 它的状态转移方程形式一般为dp[i][j]=min(dp[i][k]+dp[k+1] ...

  2. 区间dp(入门题)

    区间dp:顾名思义就是在区间上进行动态规划,通过合并小区间求解一段区间上的最优解. 常见模板: for(int len=1;len<n;len++){//区间长度 for(int be=1;be ...

  3. 区间dp板子题:[noi1995]石子合并

    非常经典的区间dp模板 对于每一个大于二的区间 我们显然都可以将它拆分成两个子序列 那么分别计算对于每个取最优值即可 #pragma GCC optimize("O2") #inc ...

  4. 区间DP石子合并问题 & 四边形不等式优化

    入门区间DP,第一个问题就是线性的规模小的石子合并问题 dp数组的含义是第i堆到第j堆进行合并的最优值 就是说dp[i][j]可以由dp[i][k]和dp[k+1][j]转移过来 状态转移方程 dp[ ...

  5. 【题解】【THUSC 2016】成绩单 LOJ 2292 区间dp

    Prelude 快THUWC了,所以补一下以前的题. 真的是一道神题啊,网上的题解没几篇,而且还都看不懂,我做了一天才做出来. 传送到LOJ:(>人<:) Solution 直接切入正题. ...

  6. POJ 1160 经典区间dp/四边形优化

    链接http://poj.org/problem?id=1160 很好的一个题,涉及到了以前老师说过的一个题目,可惜没往那上面想. 题意,给出N个城镇的地址,他们在一条直线上,现在要选择P个城镇建立邮 ...

  7. B1090 [SCOI2003]字符串折叠 区间dp

    又一道区间dp,和上一篇类似,但是比他简单,这个只有两种转移方法,不是很复杂.直接判断是否为重复的串就行. 题干: Description 折叠的定义如下: . 一个字符串可以看成它自身的折叠.记作S ...

  8. LightOJ 1422 Halloween Costumes 【 区间dp 】

    区间dp的第一题----- 看题解看了好多~~终于看懂了---55555 dp[i][j] 表示第i天到第j天至少需要多少件衣服 那么第i件衣服只被第i天占用的话, dp[i][j] = dp[i+1 ...

  9. hdu 5900 区间dp

    题意:给你n对pair 里面有两个值,分别是key 和 val .你可以取相邻的两个pair 获得其中的val,前提是两个pair 的key 的 gcd 不为 1.当然你把相邻的两个取走了之后原本不相 ...

随机推荐

  1. Asp.Net HttpContext.RemapHandler 用法

    最近在看HttpHandler映射过程文章时发现Context对象中有一个RemapHandler方法,它能将当前请求映射到指定的HttpHandler处理,可跳过系统默认的Httphandler.它 ...

  2. Maven、SecureCRT使用问题汇集

    1 Maven 无法下载pom文件中相关的依赖包 该问题可能有很多原因,我的原因是host中的localhost被修改了,改回来即可! 看起来好像出了一些网络原因的问题,顺着这个方向搜索,发现国外也有 ...

  3. CSS高效开发实战:CSS 3、LESS、SASS、Bootstrap、Foundation --读书笔记(4)构造尺寸更灵活的背景

    相比传统的图片背景来说,使用CSS构造背景色不仅可以降低网络传输的开销,更由于其尺寸的可控性受到开发者的青睐. 如设计师设计了一张背景图片作为标题背景,如图5.18所示.对于用电脑浏览网页的用户来说, ...

  4. 一些PHP性能优化汇总

    PHP优化对于PHP的优化主要是对php.ini中的相关主要参数进行合理调整和设置,以下我们就来看看php.ini中的一些对性能影响较大的参数应该如何设置. # vi /etc/php.ini (1) ...

  5. pathinfo()、dirname()、basename()获得文件的路径,名称等信息说明

    在PHP中,若想通过函数获得一个文件的路径.名称,或者是扩展名等,是非常容易的一件事.可以使用dirname().basename().pathinfo()等多种途径获得相应的信息. 假设现在有一个图 ...

  6. 为何Apache下.htaccess不起作用,Linux、Windows详解

    可能出现下面这三种的错误可能性: 第一种:启用 rewrite 和 .htaccess 设置 rewrite设置:找到apache的配置文件httpd.conf文件,找到:#LoadModule re ...

  7. GitHub使用教程

    一直以来都想使用Git来管理自己平时积累的小代码,就是除了工作之外的代码了.有时候自己搞个小代码,在公司写了,就要通过U盘或者网盘等等一系列工具进行Copy,然后回家才能继续在原来的基础上作业.Cop ...

  8. java26

    1:网络编程(理解)    (1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享    (2)网络编程模型    (3)网络编程的三要素        A:IP地址           ...

  9. GIT学习

    git init git add . git commit -m "" git status git diff 工作区->版本库->暂存区stage.master分支. ...

  10. 为 placeholder 自定义样式

    textarea::-webkit-input-placeholder{ padding: 1em; } textarea::-moz-placeholder{ padding: 1em; } 同理, ...