一.什么是区间DP?
顾名思义:区间dp就是在区间上进行动态规划,求解一段区间上的最优解。主要是通过合并小区间的 最优解进而得出整个大区间上最优解的dp算法。
二.伪代码:
for (int len = 2 -> n) //枚举长度
{
for (int i = 1 -> n-len+1) //枚举起点
{
int end = i+len-1;
for (int j = i -> end) //枚举分割点
dp[i][end]=max(dp[i][end],dp[i][j]+dp[j+1][end]+something);
}
}
三.线性区间DP
例题:石子归并
DP方程式:
dp[i][end] = min(dp[i][j]+dp[j+1][end])+sum[end]-sum[i-1];
第i堆到第end堆的合并的最小分数可以看做是:
最小的(左堆最小分数+右堆最小分数+合并分数)
code:
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAXN 0x3f3f3f3f
int s[110],sum[110],dp[110][110],
n;
int main() {
cin >> n;
memset (sum,0,sizeof sum);
memset (dp,MAXN,sizeof dp);
for (int i = 1; i <= n; i++)
{
cin >> s[i];
sum[i] = sum[i - 1]+s[i]; //重量,即分数
dp[i][i] = 0;
}
for (int len = 2; len <= n; len++) //枚举长度
{
for (int i = 1; i <= n-len+1; i++) //枚举起点
{
int end = i+len-1;
for (int j = i; j <= end; j++) //枚举分割点
dp[i][end]=min(dp[i][end],dp[i][j]+dp[j+1][end]+sum[end]-sum[i-1]); //寻找最小值
}
}
cout << dp[1][n];
return 0;
}
四.变式:
例题:石子合并
很明显,本题是上题的进化版,无论选择哪个断点,都有可能是最优解,甚至是最烂解 。然而这并不重要,因为我们可以将它转化为线性,通过开两倍数组解决这个问题。
code:
#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXN 0x3f3f3f3f
using namespace std;
int s[410],sum[410],dp[410][410],
n,ans;
int main() {
cin >> n;
memset(sum,0,sizeof sum);
memset(dp,MAXN,sizeof dp);
for (int i = 1; i <= n; i++) {
cin >> s[i];
sum[i] = sum[i - 1]+s[i];
dp[i][i] = 0;
}for (int i = n+1; i <= 2*n; i++) { //环状转化为线性
s[i] = s[i - n];
sum[i] = sum[i - 1]+s[i];
dp[i][i] = 0;
}for (int len = 2; len <= n; len++) {
for (int i = 1; i <= 2*n-len+1; i++) {
int end = i+len-1;
for (int j = i; j <= end; j++)
dp[i][end] = min(dp[i][end],dp[i][j]+dp[j+1][end]+sum[end]-sum[i-1]);
}
}ans = MAXN;
for (int i = 1; i <= n; i++) //寻找最小值
ans = min (ans,dp[i][i+n-1]);
cout << ans;
return 0;
}