今天学长给我们讲了区间dp,当然听得云里雾里,讲完之后基本处于自闭状态,然后还是自己到大佬的博客,然后看博客,但是并没有找到很详细的博客,所以我想自己写一写,大神们勿喷哈.
一 .定义
区间DP,顾名思义是在区间上DP,它的主要思想就是先在小区间进行DP得到最优解,然后再利用小区间的最优解合并求大区间的最优解。
//一般区间DP实现代码
规定 dp[i][j] 为区间[i,j] 上的最优解
for (int i = 1; i <= n; i++) //区间长度为1的初始化
dp[i][i] = x; // x视情况而定
for (int len = 2; len <= n; len++) //枚举区间长度
{
for (int i = 1, j = len; j <= n; i++, j++) //区间[i,j]
{
//DP方程实现
}
}
1. 石子合并
有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。
引入区间dp
区间dp的思想是 利用一次次的合并过程去更新所得的结果,以便下一次合并使用。
从小的区间一点一点分析
设 A[i] 为第i堆的石子数目
1.若有一堆石子,无需合并 花费为 0
2.若有两堆石子,将这两堆合并 花费为A[0] + A[1]
3.若有三堆石子,有两种情况
4.若有四堆石子,有三种情况 ……
分析一下算法过程
dp 二维数组 ,横坐标代表区间起点,纵坐标代表区间终点 . 比如: dp[1][2] 代表 第一堆石子到第二堆石子的最优解,下面详细说吧,
可能这样提前说不太理解.
首先 最外层循环 控制石子堆的长度(len = 2 to n)
for(int len = 2;len<=n;++len)
第二层循环,确定区间起点与终点
for(int i=1,j=len;j<=n;++i,++j)
经过分析还需要一层循环,枚举将区间分割的那个点k
for(int k = i;k 最后来看这个动态转移方程 dp[i][j] : 区间[i,j]上的石子合并最小花费 sum[i][j]: 区间[i,j] 的和 dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j] + sum[i][j]); 动态转移方程可以这么理解 现在已经将第 i 堆到第k堆 、 第k+1堆到第j堆合并了,现在只剩下这两堆 要将这两堆去合并 那么就是利用之前算出来的dp[i][k] + dp[k+1][j] 再加上区间[i,j]的和 为什么是加区间[i,j]的和呢 你把[i,k] 和[k+1,j]的石子合并到了一起 合并这两个区间的花费一定是这两个区间的总石子数,当然也就是[i,j]总石子数。 过程 : 1 初始化 dp数组 整个过程就这样的 ,不断的更新小区间最优解,然后最总就可以得到大区间的最优解. 链接 : https://vjudge.net/problem/51Nod-1021 代码 : 今天先写到这里,等学了其他的再加补充. #include