前言:这两天没有写什么题目,把前两周做的有些意思的背包题和最长递增、公共子序列写了个总结。反过去写总结,总能让自己有一番收获......就区间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. EXPLAINING WHAT ACTION AND FUNC ARE

    http://simpleprogrammer.com/2010/09/24/explaining-what-action-and-func-are/ Explaining What Action A ...

  2. Maven总结

    项目管理构建工具:maven ant gradle == 项目管理利器(Maven)——maven介绍及环境搭建maven可以帮助我们更有效地管理项目,它也是一套强大的自动化构建工具,覆盖了编译.测试 ...

  3. [Recommendation System] 推荐系统之协同过滤(CF)算法详解和实现

    1 集体智慧和协同过滤 1.1 什么是集体智慧(社会计算)? 集体智慧 (Collective Intelligence) 并不是 Web2.0 时代特有的,只是在 Web2.0 时代,大家在 Web ...

  4. There are no resources that can be added or removed from the server

    第1步.新建一个“Dynamic Web Project” 第2步.把新建项目里面的.project文件和.settings文件夹复制到导入的那个项目里面. 第3步.把web projiect set ...

  5. 如何使用Service的Context弹出Dialog对话框,即全局性对话框

    在dialog.show()语句前加入: dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 然后在An ...

  6. 基于jquery fly插件实现加入购物车抛物线动画效果,jquery.fly.js

    在购物网站中,加入购物车的功能是必须的功能,有的网站在用户点击加入购物车按钮时,就会出现该商品从点击出以抛物线的动画相似加入购物车,这个功能看起来非常炫,对用户体验也有一定的提高.下面介绍基于jque ...

  7. [BZOJ4408][Fjoi 2016]神秘数

    [BZOJ4408][Fjoi 2016]神秘数 试题描述 一个可重复数字集合S的神秘数定义为最小的不能被S的子集的和表示的正整数.例如S={1,1,1,4,13},1 = 12 = 1+13 = 1 ...

  8. AngularJS ui-router (嵌套路由)

    http://www.oschina.net/translate/angularjs-ui-router-nested-routes AngularJS ui-router (嵌套路由) 英文原文:A ...

  9. SQLPULS : 密码中有特殊字符的处理方法

    前日在使用SQLPLUS访问oracle数据库时,系统提示密码过期,需要更新密码.于是不假思索的修改密码为xxx@2016(估计当时脑子抽风了),造成了杯具的开始. 再次进入SQLPLUS,输入用户名 ...

  10. jQuery UI Datepicker

    http://www.runoob.com/try/try.php?filename=jqueryui-example-datepicker-dropdown-month-year <!doct ...