四边形不等式优化
四边形不等式优化
填坑
简介
在动态规划问题中
我们常遇到这样一类问题,它的dp方程长这样
这样我们的复杂度一般是\(O(n^3)\)的
设\(a < b <= c < d\)
若有\(f[a][c]+f[b][d]<=f[b][c]+f[a][d]\)
则称这个东西满足四边形不等式
定理
设w函数为价值函数,对于上题来说\(w(i,j) = cost[i][j]\)
若\(w(i,j)\)满足\(w(a,d)>=w(b,c)\)则称w关于区间包含关系单调
定义s(i,j)表示dp(i,j)的最优决策点
有如下定理
1.若w同时满足区间包含单调性与四边形不等式则f也四边形不等式
2.若f满足四边形不等式则s单调即\(s(i,j)\leq s(i,j+1)\leq s(i+1,j+1)\)
3.w满足四边形不等式当且仅当\(w(i,j)+w(i+1,j+1)<=w(i,j+1) + w(i+1,j)\)
怎么用呢?
原先在dp的时候,枚举k的范围是\((i,j)\),转移复杂度是\(O(n)\)
若f满足四边形不等式的话那么\(s[i][j-1] \leq s[i][j] \leq s[i+1][j]\)
那么我们倒退i正推j,脑补一下,那么转移复杂度均摊是\(O(1)\)的了
所以你只需要证明w为凸的且单调,dp时记录一下s,就可以降一维复杂度惹
证明w满足四边形不等式
不会(逃
注意,可以用四边形不等式优化的dp转移方程并不是只有上述一种
如:对于
这个方程来说
若证明w为凸的,那么f也为凸的,我们也可以用四边形不等式优化
关键是怎么证明呀
同mlystcall
我们可以知道如何绕过证明
如果我们觉得一个方程能用四边形不等式优化,就把他的所有决策点,也就是s矩阵打印出来,观察一下在每行每列上是否单调,如果单调,就说明这个方程可以用四边形不等式优化
其次要注意几点
1.应注意决策点在那些范围内单调,这点在实际问题中应该很容易体现出来,比如在区间dp中的决策点\(s[i][j] | j>i\)的情况是不存在的
2.注意枚举顺序
那么剩下的就是
打表检验单调性了
据说与四边形不等式相关的优化,还有一个叫凸完全单调性,也是证明之后利用决策单调性优化。不会不会...
附上石子合并的的代码
w满足四边形不等式,因为此时相交区间和等与包含区间和
其中求最大w的不能满足单调性?然后决策只在区间两断处取到?
#include<cstdio>
#include<algorithm>
inline int read() {
int x = 0,f = 1;
char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1;c = getchar();}
while(c <= '9' && c >= '0') x = x * 10 + c - '0',c = getchar();
return x * f;
}
const int maxn = 2007;
int dp[maxn][maxn],s[maxn][maxn],cnt[maxn];
int sum[maxn];
int dpm[maxn][maxn];
int main () {
int n = read();
for(int i = 1;i <= n;++ i) cnt[n + i] = cnt[i] = read(),sum[i] = sum[i - 1] + cnt[i];
for(int i = n + 1;i <= n * 2;++ i) sum[i] = sum[i - 1] + cnt[i];
for(int i = 1;i <= 2 * n;++ i) s[i][i] = i;
for(int i = n * 2 - 1;i >= 1;-- i) {
for(int j = i + 1;j <= n * 2;++ j) {
int tmp = 0x7fffffff,loc = 0;
dpm[i][j] = std::max(dpm[i][j - 1],dpm[i + 1][j]) + sum[j] - sum[i - 1];
for(int k = s[i][j - 1];k <= s[i + 1][j];++ k) {
if(tmp >dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1]) {
tmp = dp[i][k] + dp[k + 1][j] + sum[j] - sum[i - 1];
loc = k;
}
}
dp[i][j] = tmp;
s[i][j] = loc;
}
}
int ansm = 0x7fffffff,ansx = 0;
for(int i = 1;i <= n;++ i) {
ansm = std::min(dp[i][i + n - 1],ansm) ;
ansx = std::max(dpm[i][i + n - 1],ansx) ;
}
printf("%d\n%d",ansm,ansx);
return 0;
}