众所周知,DP优化有单调队列优化、数据结构优化、矩阵快速幂优化、斜率优化、四边形不等式优化、决策单调性优化、凸优化等。本文讲解关于一类DP方程的三种优化情况。
其中 且 、 单调不减。 可换为 ,方法相同。
与 无关
对于 ,若 不比 更劣(更大),则 在 以后的更新都不会有任何贡献,因为有 的地方就有 ,且完全可以选 而不选 。因此用一个单调队列维护当前有用的转移下标即可。
时间复杂度 。
只含 的多项式、 项和 项
时间复杂度为 或 。
满足四边形不等式
四边形不等式
这个性质说明,对于更大的
,
随着
增长的速度越慢。因此对于
,如果从
转移不劣于从
转移,那么
在
转移以后就没用了,因为随着
的增加
只会比
越来越差。即
的决策点是单调右移的,即决策单调性。
一般有两种方法来实现:
栈+二分
考虑每个决策点能更新哪些状态。我们枚举 到 的决策点来更新后面的状态。由于决策单调性,每次更新后,决策点数列是单调不减的。因此每次更新的位置是一个区间 。于是我们用一个栈维护从左到右的连续相同的决策点块,每次更新时从栈顶开始检查,如果新决策比原决策块完全更优就把原决策块弹掉,否则二分原决策块区间找到从哪个点开始新决策更优。最后将新决策块入栈。
每个决策最多入栈、出栈一次,因为用了二分,时间复杂度为 。
struct decis {
int a, l, r; // 决策点位置,决策块管辖区间
}S[maxn];
int top;
//...
{
top = 0;
S[top++] = (decis){0, 0, n-1}; // 第一个决策块
for(int i = 1; i < n; i++) {
while(top) {
decis ctop = S[top-1];
int ftop = f[ctop.a] + calc(), fnew = f[i] + calc(); //calc()为分别用ctop.a和i更新ctop.l的代价
if(ftop > fnew) top--; else break;
}
if(top) {
int l = S[top-1].l, r = S[top-1].r+1;
while(l < r) {
int mid = l + r >> 1;
int ftop = f[S[top-1].a] + calc(), fnew = f[i] + calc(); //calc()为分别用S[top-1].a和i更新mid的代价
if(ftop > fnew) r = mid; else l = mid+1;
}
// 此时l的值是第一个新块较优的位置
if(l <= S[top-1].r) {
decis o = S[top-1]; top--;
S[top++] = (decis){o.a, o.l, l-1};
}
if(l <= n-1) S[top++] = (decis){i, l, n-1};
}else S[top++] = (decis){i, k-1, n-1};
}
for(int i = 0; i < top; i++) for(int j = S[i].l; j <= S[i].r; j++) f[j] = f[S[i].a] + calc();
}
分治
考虑分治处理每个点的决策点。设当前在处理 的决策点,并已知可能的决策点区间为 。设 ,则先暴力求出 的决策点 (范围肯定是 ),然后分治处理两边,显然 的决策点在 ,而 的决策点在 。复杂度为 。
满足四边形不等式和区间单调性
Upd on 2018.11.26
区间单调性 对于所有区间 包含 ,有 。
若 满足四边形不等式和区间单调性,则决策点满足区间单调性。因此枚举决策点的时候只用从之前的决策点之间枚举。类似四边形不等式优化区间DP,复杂度从 降为 。
例题:IOI2000 邮局
练习题
BZOJ3675 POJ1160 BZOJ1492 HDU3480 BZOJ2726 HDU2829 BZOJ1791 HDU3516
2018.11.30 DP凸优化