动态规划优化

时间:2022-01-30 11:12:45

单调队列

简述

函数 f[i] ,转移如下:
f[i]=min(g[j]) 其中 g[j] 是关于 j f[j] 的一个函数,且 b[i]j<i
维护一个单调递增的队列,队列中的元素下表保证关于 i 合法,即 b[i]j<i 。每次决策前删除队首不合法的元素,决策时取队首元素转移,决策后将当前值加入队尾并保证单调性即可。复杂度为 O(n)

四边形不等式

简述

函数 f[i][j] ,转移如下:
f[i][j]=min(f[i][k]+f[k+1][j]+w[i][j]) 其中 ik<j
若函数 w[i][j] 具有以下两种性质:
1、当 ab<cd w[b][c]w[a][d]
2、当 ab<cd 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

设有 n 堆沙子,每堆沙子都有一定的质量,现在要将 n 堆沙子归并成一堆,归并的过程为每次将相邻的两堆沙子合成一堆,这样经过 n1 次归并后将只剩下一堆沙子。两堆沙子的质量的和为归并两堆沙子的代价,现求进行 n1 次归并的最小代价。

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;
}