区间DP经典模型

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区间DP经典模型相关的知识,希望对你有一定的参考价值。

http://blog.csdn.net/y990041769/article/details/24238547

先附上一个链接 后面有引用的代码

概述

区间 DP:是指在一段区间上进行的一系列动态规划。

对于区间 DP 这一类问题,我们需要计算区间 [1,n] 的答案,通常用一个二维数组 dp 表示,其中 dp[x][y] 表示区间 [x,y]。

有些题目,dp[l][r] 由 dp[l][r-1] 与 dp[l+1][r] 推得;

也有些题目,我们需要枚举区间 [l,r]内的中间点,由两个子问题合并得到,也可以说 dp[l][r] 由 dp[l][k] 与 dp[k+1][r] 推得,其中 lk<r。

对于长度为 n 的区间 DP,我们可以先计算 [1,1],[2,2][n,n] 的答案,再计算 [1,2],[2,3][n?1,n],以此类推,直到得到原问题的答案。

 

例 1:石子归并问题

题目描述

当前有 N 堆石子,他们并列在一排上,每堆石子都有一定的数量。

我们需要把这些石子合并成为一堆,每次合并都只能把相邻 的两堆合并到一起,

每一次合并的代价都是这两堆石子的数量之和,经过 N-1次合并后成为一堆。求把这些石子合并成一堆所需的最小代价。

解析

根据动态规划的思想,我们只要求出每两堆石子合并的最小代价,然后再求出每三堆石子合并的最小代价,

并以此类推就能最终求出 n 堆石子合并的最小代价。

我们把 dp[i][j] 定义为合并第 i 堆石子到第 j 堆石子所需的最小代价。

很容易就能得到 dp[i][j] = min(dp[i][k] + dp[k+1][j])

显然通过这个式子,我们可以按代价从小到大的顺序进行枚举来不断让石子进行合并,最终就能获得合并成一堆石子的最小代价。

时间复杂度是 O(n?3??)。

memset(dp, 0, sizeof(dp));
for (int l = 2; l <= n; ++l) {
    for (int i = 0, j; i <= n - l; ++i) {
        j = i + l - 1;
        dp[i][j] = inf;
        for (int k = i; k < j; ++k) {
            dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
        }
    }
}

 

例 2:括号匹配问题

题目描述

当前有一个串,它由 ‘(‘,‘)‘,‘[‘,‘]‘ 四种括号所组成,其中每种括号的次序或数量不定,

求最少向这个串中插入多少个括号就能够使整个串中所有的括号左右配对。如:当前的串为 "( [ ] ] )",向其中添加一个 ‘[‘ 就能达到我们的要求。

解析

我们可以求出当前这个串中,能够一一配对的括号的总数,这里称之为最大匹配,然后再用串的总长度减去最大匹配就能够得到我们需要的答案。

如:当前的串为 "( [ ] ] )",它的最大匹配就是 4

因此,现在我们的问题就转变成了如何求出当前这个串的最大匹配数目。我们可以用 dp[i][j] 来表示从第 个括号到第 个扩号之间的最大匹配数目。

然后根据第 i 个和第 j 个括号是否匹配,以及 dp[i][j] 的值很容易的得出 dp[i+1][j?1]了。

我们可以枚举所有的 dp[i][j],来求出这个串的最大匹配数,再用这个串的总长度 - 最大匹配数就是我们所求的答案。

在枚举过程中:

 

dp[i][j] = judge(i,j) ? dp[i+1][j-1] + 2 : dp[i + 1][j - 1]; //judge()函数功能为判断第i个括号与第j个括号是否匹配,若匹配返回 `true` ,否则返回 `false`。
 

并且 dp[i][j] 也可以通过把区间 [i,j],分成区间 [i][k] 和区间 [k+1][j] 来得到,即:dp[i][j] = dp[i][k] + dp[k+1][j] 。我们枚举 k ,取最大值即可。

状态转移方程为:dp[ i ][ j ] = max ( dp [ i ][ j ] , dp [ i ][ k ] + dp [ k+1 ][ j ] )

 引用的代码
#include <iostream>  
#include <cstring>  
#include <algorithm>  
#include <string>  
using namespace std;  
const int  N = 120;  
int dp[N][N];  
int main()  
{  
    string s;  
    while(cin>>s)  
    {  
        if(s=="end")  
            break;  
        memset(dp,0,sizeof(dp));  
        for(int i=1;i<s.size();i++)  
        {  
            for(int j=0,k=i;k<s.size();j++,k++)  
            {  
                if(s[j]==(&&s[k]==) || s[j]==[&&s[k]==])  
                    dp[j][k]=dp[j+1][k-1]+2;  
                for(int f=j;f<k;f++)  
                    dp[j][k]=max(dp[j][k],dp[j][f]+dp[f+1][k]);  
            }  
        }  
        cout<<dp[0][s.size()-1]<<endl;  
    }  
    return 0;  
}  

 

以上是关于区间DP经典模型的主要内容,如果未能解决你的问题,请参考以下文章

区间DP小结(附经典例题) 转载

状压dp,区间dp,矩阵快速幂

ZOJ3469 Food Delivery (经典区间dp)

石子合并2——区间DP这是道经典入门例题/试手模板

区间dp

石子合并问题,经典区间DP