代码
一维DP
// 石子合并模板代码
for (int i = 1; i <= n; i++) dp[i][i] = 0; // 初始化
for (int len = 2; len <= n; len++) {
for (int i = 1; i <= n - len + 1; i++) { // 倒数第len个数是要当作起点的
int j = i + len - 1; // 因为还包含i本身
dp[i][j] = INF; // 初始为极大值,因为要求极小值
for (int k = i; k < j; k++) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + w[i][j]); // 使用k将i、j分隔开
} // 此处的w 是说 ; 因为我们分开了成了两堆,所以要将他们合并起来需要付出的代价
// 但有时候是没有这个w的,需要看题目判断
}
}
二维DP(弄成两个一维DP)
// CF199F
for (int lenx = 1; lenx <= n; lenx++) {
for (int leny = 1; leny <= n; leny++) {
for(int x1 = 1; x1 <= n - lenx + 1; x1++) {
for (int y1 = 1; y1 <= n -leny + 1; y1++) {
int x2 = x1 + lenx - 1;
int y2 = y1 + leny - 1;
if (x1 == x2 && y1 == y2) continue; // 这种情况不用看了,单纯一个点的情况前面都初始化过了
dp[x1][y1][x2][y2] = max(abs(x1 - x2), abs(y1 - y2)) + 1; // 初始值
for(int k = x1; k <= x2; k++) {
dp[x1][y1][x2][y2] = min(dp[x1][y1][x2][y2], dp[x1][y1][k][y2] + dp[k + 1][y1][x2][y2]);
}
for (int k = y1; k <= y2; k++) {
dp[x1][y1][x2][y2] = min(dp[x1][y1][x2][y2], dp[x1][k][x2][y2] + dp[x1][k + 1][x2][y2]);
}
}
}
}
}
原理
区间 DP 是常见的 DP 应用场景。区间 DP也是线性 DP,它把区间当成 DP 的阶段,用区间的两个端点描述状态和处理状态的转移。区间DP的主要思想是先在小区间进行DP得到最优解,然后再合并小区间的最优解求得大区间的最优解。解题时,先解决小区间问题,然后合并小区间,得到更大的区间,直到解决最后的大区间问题。合并的操作一般是把左右两个相邻的子区间合并。
核心
分割:dp[i][j] = max(dp[i][k], dp[k + 1][j]);
从区间的关系两边得到转移方程
题目
1、石子合并问题(网上找来做)
2、刷字符串 hdu 2476
3、栈的 hdu 4283 (若第i个位置(序列排列)的元素第k个出栈,则i 后的 k - 1 个元素一定比i先出栈)
4、CF1199F
练习:
https://leetcode-cn.com/circle/article/NfHhXD/
区间DP:https://vjudge.net/contest/77874
洛谷p 1880 3146 1063 1005 4170 4302 2466