Tree Construct 四边形不等式

lindongli2004

2020-07-20 20:28:41

Personal

### 题目大意 给定平面上 $n$ 个点 $(x_i,y_i)$,满足 $x_i$ 单调递增, $y_i$ 单调递减。求一棵最小生成树,连边方向只能按 $x$ 轴 $y$ 轴方向。 ### 题解报告 设 $f[i][j]$ 表示将 $i\rightarrow j$ 这几个点联通的最小生成树边权和,就有以下的转移:$f[i][j]=min\{f[i][k]+f[k+1][j]+(x[k+1]-x[i]+y[k]-y[j])\}$ 直接枚举左右端点和中间点转移,复杂度 $O(n^3)$ 我们可以用四边形不等式优化,一般地,若转移方程 $f[i][j]=max/min\{f[i][k]+f[k+1][j]+w[i][j]\}$ 满足 $\forall i,i',j,j',i\leq i'\leq 'j \leq j$ 有 1.$w(i,j)\geq w(i',j')$ 2.$ w(i,j)+w(i',j')\geq w(i,j')+w(i',j)$ 那么dp的决策点 $s[i][j]$ 满足 $s[i][j-1]\leq s[i][j]\leq s[i+1][j]$ 好的,我们猜想,我们的转移方程也满足上述四边形不等式的性质。经过实践,是正确的,所以我们记下每次的决策点进行转移,复杂度 $O(n^2)$ code below: ```cpp #include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define for1(a, n) for (int (a) = 1; (a) <= (n); (a)++) #define ysk(x) (1<<(x)) const int INF=0x3f3f3f3f; const int maxn=1000; int n,s[maxn+10][maxn+10],dp[maxn+10][maxn+10]; struct Node{int x,y;}a[maxn+10]; int main() { std::ios::sync_with_stdio(false); while(cin>>n){ for1(i,n)cin>>a[i].x>>a[i].y; for1(i,n)dp[i][i]=0,s[i][i]=i; for(int add=1;add<n;add++){ for(int le=1;le+add<=n;le++){ int ri=le+add; int L=s[le][ri-1]; int R=s[le+1][ri]; dp[le][ri]=INF; for(int k=L;k<=R&&k<ri;k++){ int ret=dp[le][k]+dp[k+1][ri]+a[k+1].x-a[le].x+a[k].y-a[ri].y; if(ret<=dp[le][ri]) dp[le][ri]=ret,s[le][ri]=k; } } } printf("%d\n",dp[1][n]); } return 0; } ``` ### 写在最后 这种方法的证明非常困难,所以在OI中一般都是拿来直接试试看,就算不行,也可以用来骗分,是一种“优化”dp有力的工具。