区间dp总结篇

来源:互联网 发布:短信群发网站源码 编辑:程序博客网 时间:2024/05/21 18:46

原文连接 :区间dp总结篇 - 紫忆 - 博客园
http://www.cnblogs.com/ziyi--caolu/archive/2013/08/04/3236035.html

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

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).....

 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]取上面这些情况的最小值.........

 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的,都需要输出它自身以及与它自身匹配的括号.........

 5、poj1651(推荐)

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

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

 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)

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

 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));   
 最后一行,最后一列的那个值就是最小编辑距离 

 

0 0