单调队列
简述
函数 f[i],转移如下:
f[i]=min(g[j]) 其中 g[j] 是关于 j 或
维护一个单调递增的队列,队列中的元素下表保证关于 i 合法,即
四边形不等式
简述
函数 f[i][j] ,转移如下:
f[i][j]=min(f[i][k]+f[k+1][j]+w[i][j]) 其中 i≤k<j
若函数 w[i][j] 具有以下两种性质:
1、当 a≤b<c≤d 时 w[b][c]≤w[a][d]
2、当 a≤b<c≤d 时 w[a][c]+w[b][d]≤w[b][c]+w[a][b]
我们称函数 w[i][j] 满足关于区间包含的单调性与四边形不等式,此时函数 f[i][j] 也将满足四边形不等式,其决策函数 g[i][j] 将满足关于区间包含的单调性。我们可以利用 g[i][j] 满足关于区间包含的单调性这一性质来缩小决策变量 k 的枚举范围,以优化dp。
一些题目
最小代价子母树
Description
设有
Solution & Code
f[i][j]=min(f[i][k]+f[k+1][j]+sum[i][j])
其中 sum[i][j] 满足关于区间包含的单调性与四边形不等式,故 f[i][j] 满足四边形不等式,其决策函数 g[i][j] 满足关于区间包含的单调性,所以可以用四边形不等式加速。
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 105;
const int inf = 1e9;
int n, w[maxn], s[maxn], f[maxn][maxn], g[maxn][maxn];
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
for(int i = 1; i <= n; ++i) s[i] = s[i-1] + w[i];
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
f[i][j] = inf;
for(int i = 1; i <= n; ++i) f[i][i] = 0, g[i][i] = i;
for(int l = 2; l <= n; ++l)
for(int i = 1, j = i + l - 1; j <= n; ++i, ++j)
for(int k = g[i][j-1]; k <= g[i+1][j] && k < j; ++k){
int tmp = f[i][k] + f[k+1][j] + s[j] - s[i-1];
if(f[i][j] > tmp){
f[i][j] = tmp;
g[i][j] = k;
}
}
printf("%d\n", f[1][n]);
return 0;
}