当你遇到这样的转移方程:dp[i][j] = min(dp[i][k-1], dp[k][j]) + w[i][j] 且n较大时。。。
低情商:无脑三层 for,喜提 TLE (暴力美学)
高情商:这不是有手题??? (四边形不等式优化)
虽然它俩都是三层for,但是后者的复杂度却只有O(N^2),这就是四边形不等式能干的事。
先放结论:
形如 dp[i][j] = min(dp[i][k-1], dp[k][j]) + w[i][j],其中 i <= k <= j,dp[i][j]表示区间i到j上的最值(此处以最小值为例,也可以为最大值)。
可以通过四边形不等式优化该方程,可将时间复杂度O(N ^ 3)优化至O(N ^ 2)
先抛出两个概念
1. 区间单调性
若满足 a <= b <= c <= d,且 w[b][c] <= w[a][d]。那么w具有区间单调性。
2. 四边形不等式
若满足 a <= b <= c <= d,且 w[a][c] + w[b][d] <= w[a][d] + w[b][c] 。那么w满足四边形不等式。
接着引入一个数组 s[i][j] ,表示dp[i][j] 取得最优值时对应的下标。
再来两个性质:
- 若 w[ ][ ] 同时满足上面提到的区间单调性和四边形不等式,则 dp[ ][ ] 满足四边形不等式。
- 若 dp[i][j] 满足四边形不等式,则 s[i][j] 单调,即 s[i][j-1] ≤ s[i][j] ≤ s[i+1][j]。
那么,现在的问题就成了求 s[ ][ ]。而根据 s[ ][ ] 的定义,应该不难求出来。
看到这里应该有想法了吧?且往下看
原本暴力的循环 ↓
for (int j = 1; j <= n; j++)
for (int i = j; i >= 1; i--)
for (int k = i; k < j; k++)
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);
四边形不等式优化 ↓
for (int j = 1; j <= n; j++)
for (int i = j; i >= 1; i--)
for (int k = s[i][j-1] ; k <= s[i+1][j] ; k++) {
if(dp[i][j] > dp[i][k]+dp[k+1][j]+w[i][j]) {
dp[i][j] = dp[i][k]+dp[k+1][j]+w[i][j];
s[i][j] = k;
}
}
为啥这样能做到O(N^2)呢?
这是因为枚举的区间长度 len = j-i,而 s[i,j-1] 和 s[i+1,j] 的长度是 len-1,是上一次已经计算过的,可以直接调用,所以时间复杂度为 O(N^2)。
—end—