四边形不等式 学习笔记
一维
这个基本上可以用斜率优化搞定,可以看看前面的李超树。
但是如果遇到了转移方程出现了同时包含关于 \(j\) 的量与多个次数不同的关于 \(i\) 的量相乘的话,斜率优化就挂了,这个时候需要用一些一维四边形不等式的奇技淫巧,比如分治、二分队列、LARSCH 啥的,我先没学。
二维
这个用起来简单。考虑以下经典的区间 DP 转移方程(即石子合并):
然后证明过程不需要记,只需要知道如果 \(w\) 矩阵满足 \(w_{i,j} + w_{i+1,j+1} \le w_{i,j+1} + w_{i+1,j}\),那么说明区间 \([l,r]\) 的最优决策点矩阵 \(s_{l,r}\) (\(l \le s_{l,r} \le r - 1\))是单调的,最优决策点的意思就是说 \(f_{l,r} = f_{l,s_{l,r}} + f_{s_{l,r} + 1,r} + w_{l,r}\) 这样转移是最优的,单调的意思就是对于矩阵的每一行,\(s_{l,r-1} \le s_{l,r}\);对于每一列,\(s_{l,r} \le s_{l+1,r}\)。这样一来,区间 \([l,r]\) 的枚举就缩小到了 \(s_{l,r-1}\) 到 \(s_{l+1,r}\),然后加一下发现很多项抵消了,最后是 \(\mathcal{O}(n^2)\) 的。
枚举的话就直接
for(int l = n; l >= 1; l ++)
for(int r = l + 1; r <= n; r ++)
for(int k = s[l][r - 1]; k <= s[l + 1][r]; k ++)
然后边转移边记录当前最优决策点即可,枚举到每一个区间,需要的上下界就肯定计算好了。
如果改求 \(\max\) 的话,证明 \(w_{i,j} + w_{i+1,j+1} \ge w_{i,j+1} + w_{i+1,j}\) 就行。我的记忆方法就是,求 \(\min\),左上和右下之和就要小;求 \(\max\),左上和右下之和就要大。
合并石子(加强版)代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2007;
int n, a[N], f[N][N], s[N][N];
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++)
cin >> a[i], a[i + n] = a[i];
for(int i = 1; i <= (n << 1); i ++)
f[i][i] = 0, s[i][i] = i, a[i] += a[i - 1];
for(int l = n; l >= 1; l --)
for(int r = l + 1; r - l + 1 <= n; r ++){
f[l][r] = 1e18;
for(int mid = max(l, s[l][r - 1]); mid <= min(r - 1, s[l + 1][r]); mid ++)
if(f[l][r] > f[l][mid] + f[mid + 1][r] + a[r] - a[l - 1])
f[l][r] = f[l][mid] + f[mid + 1][r] + a[r] - a[l - 1], s[l][r] = mid;
}
int ans = 1e18;
for(int i = 1; i <= n; i ++) ans = min(ans, f[i][i + n - 1]);
cout << ans << "\n";
return 0;
}
本文来自博客园,作者:GE9x,转载请注明原文链接:https://www.cnblogs.com/GE9X/p/19763560

浙公网安备 33010602011771号