Tree Construction
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 684 Accepted Submission(s): 364
Problem DescriptionConsider a two-dimensional space with a set of points (xi, yi) that satisfy xi < xj and yi > yj for all i < j. We want to have them all connected by a directed tree whose edges go toward either right (x positive) or upward (y positive).
The figure below shows an example tree.
Write a program that finds a tree connecting all given points with the shortest total length of edges.
InputThe input begins with a line that contains an integer n (1 <= n <= 1000), the number of points. Then n lines follow. The i-th line contains two integers xi and yi (0 <= xi, yi <= 10000), which give the coordinates of the i-th point.
OutputPrint the total length of edges in a line.
Sample Input
51 52 43 34 25 1110000 0
Sample Output
120
Source2010 ACM-ICPC Multi-University Training
Contest(8)——Host by ECNU
Recommendzhouzeyong 【題目意思】給你很多個點,這些點滿足a set of points (xi, yi) that satisfy xi < xj and yi > yj for all i < j.讓你用一棵樹把所有點連在一齊,樹只能往上跟右生長,求樹的總長度最小
【解題思路】類似石子合并,加上四邊形最佳化就行了
定義狀態 dp[i,j]表示點i到點j合并在一起的最小花費(樹枝的長度),
狀態轉移方程:dp[i,j]= min(dp[i,k]+dp[k+1,j]+cost(i,j) ) i<k<j
cost(i,j)=py[k]-py[j]+px[k+1]-px[i];
當j固定時,cost(i,j)單調遞減函數
我們猜測cost(i,j)滿足四邊形不等式
證明:
F(i)=cost(i,j+1)-cost(i,j)=py[j]-py[j+1]是一個與i無關的多項式,
所以j固定時,F(i)滿足四邊形不等式,得證。
s[i,j]=k;s[i-1,j] <= s[i,j] <= s[i,j+1];
由於決策s具有單調性,因此狀態轉移方程可修改為:
dp[i,j]= min(dp[i,k]+dp[k+1,j]+cost(i,j) ) s[i-1,j] <=k<= s[i,j+1];
開始輕鬆的寫出了動態方程:
dp[i][j]=MIN(dp[i][k]+dp[k+1][j]+x[k+1]-x[i]+y[k]-y[j] i<=k<=j
於是寫了一個樸素枚舉的程式。果斷逾時了。百思不得其解。搜了下題解才知道要
用四邊形不等式最佳化。即縮小枚舉範圍時間發雜度降到o(n^2)。四邊形不等式一片
空白。這是我第一個四邊形不等式最佳化dp。看了各種資料後終於明白大部分思想了
就是四邊形條件不好證明。不過簡單運用會點了。dp依然很菜。繼續加油!
#include <iostream>#include<stdio.h>#include<string.h>#define MAX(a,b) ((a)>(b)?(a):(b))#define MIN(a,b) ((a)<(b)?(a):(b))#define positive(a) ((a)>0?(a):-(a))using namespace std;int x[1010],y[1010];//記錄x,y座標int n,ans,dp[1010][1010],s[1010][1010];void solve(){ int i,j,k,len,l,r,temp; memset(dp,0x3f,sizeof dp); for(i=1;i<=n;i++) { dp[i][i]=0;//把自己串連起來肯定需要0 s[i][i]=i;//初始為k的枚舉範圍為i到i。 } for(len=2;len<=n;len++)//枚舉區間長度。由動態方程 { //dp[i][j]=MIN(dp[i][k]+dp[k+1][j]+x[k+1]-x[i]+y[k]-y[j] //可知dp[i][k],dp[k+1][j]均是比dp[i][j]小的區間為了計算dp[i][j]必須算出比 //其小的區間dp。所以從小到大枚舉區間長度 for(i=n-len+1;i>0;i--)//i必須從大到小枚舉。因為計算dp[i][j]時要用到dp[i][k],dp[k+1][j] //k+1>i所以為了保證k+1已經算出必須倒著枚舉 { j=i+len-1; l=s[i][j-1];//由四邊形不等式s[i][j-1]<=k<=s[i+1][j] r=MIN(j-1,s[i+1][j]);//計算s[i][j]需要s[i][j-1]和s[i+1][j] //s[i+1][j]前面已算出.s[i][j-1]範圍比s[i][j]小也已算出 for(k=l;k<=r;k++)//枚舉l,r,之間的k即可 { temp=dp[i][k]+dp[k+1][j]+x[k+1]-x[i]+y[k]-y[j]; if(temp<dp[i][j]) { dp[i][j]=temp; s[i][j]=k;//得出i和j之間枚舉k的範圍為i到k } } } } ans=dp[1][n];}int main(){ int i; while(~scanf("%d",&n)) { for(i=1;i<=n;i++) scanf("%d%d",x+i,y+i); solve(); printf("%d\n",ans); } return 0;}