poj 1160 Post Office(四邊形不等式優化dp)
POJ_1160
我們可以用f[i][j]表示建好i個郵局時覆蓋到第j個村莊的最優解,那麼就可以得到f[i][j]=min{f[i-1][k]+w[k+1][j]}(k<j),其中w[x][y]表示建一個郵局覆蓋x到y的村莊的距離和,w[x][y]可以事先預處理出來。
這個題目還可以用四邊形不等式去優化,實際上四邊形不等式優化難點不在於應用,只是在K[i][j-1]<=k<=K[i+1][j]中去選擇更新f[i][j]的k即可,比較複雜的部分就在於對k可以這樣選擇做出證明。
一般四邊形不等式的證明步驟如下:
①證明w為凸,這一步用黑書上的定理w為凸當且僅當w[i][j]+w[i+1][j+1]<=w[i][j+1]+w[i+1][j],這樣只要說明w[i+1][j]-w[i][j]是關於j單調遞減的即可。
②證明f為凸,這一步要利用①中的定理去證f[i][j]+f[i+1][j+1]<=f[i][j+1]+f[i+1][j],而證明的方法通常是利用w為凸的結論,先假設f[i][j+1]取得最優解是k為x,f[i+1][j]取得最優解時f[i+1][j]為y,然後分x<y和y<x兩種情況,將f[i][j]和f[i+1][j+1]各按k=x或k=y拆開之後,將拆出的w應用四邊形不等式,再將各項合併成f[i][j+1]+f[i+1][j]從而完成證明。
③證明K[i][j-1]<=K[i][j]<=K[i+1][j],證明K[i][j-1]<=K[i][j]時,要先假設f[i][j-1]取得最優解時k=y,然後利用x<=y<=j-1<j列一個四邊形不等式,然後在不等式兩邊新增一定的項試圖得到f[i][j-1](k=x)+f[i][j](k=y)<=f[i][j-1](k=y)+f[i][j](k=x),也就是f[i][j-1](k=x)-f[i][j-1](k=y)<= f[i][j](k=x)-f[i][j](k=y),這時我們就會發現因為f[i][j-1](k=y)<=f[i][j-1](k=x),那麼一定有f[i][j](k=y)<=f[i][j](k=x),也就是說對於所有小於y的x,都會有f[i][j-1](k=y)<=f[i][j-1](k=x),那麼也都會有f[i][j](k=y)<=f[i][j](k=x),因此令f[i][j]取得最優解的k一定不小於y,這樣就完成了對K[i][j-1]<=K[i][j]的證明。對於K[i][j]<=K[i+1][j]的證明是類似的。
程式碼如下:複雜度O(np)
#include<cstdio> #include<cstdio> #include<cmath> #include<queue> #include<stack> #include<string> #include<cstring> #include<iostream> #include<map> #include<vector> #include<algorithm> #include<set> #include<cmath> using namespace std; const int nn = 3100; const int inf = 0x3fffffff; int n,p; int a[nn]; int sum1[nn]; int sum2[nn]; int dp[nn][35]; int s[nn][35]; int getw(int l,int r) { int mid=(l+r)/2; return sum1[r]-sum1[mid-1]-(r-mid+1)*a[mid]+sum2[l]-sum2[mid+1]-(mid-l+1)*(a[n]-a[mid]); } int main() { int i,j,k; while(scanf("%d%d",&n,&p)!=EOF) { for(i=1;i<=n;i++) { scanf("%d",&a[i]); } sum1[0]=0; for(i=1;i<=n;i++) { sum1[i]=sum1[i-1]+a[i]; } sum2[n+1]=0; for(i=n;i>=1;i--) { sum2[i]=sum2[i+1]+a[n]-a[i]; } for(j=0;j<=p;j++) { dp[0][j]=0; s[0][j]=0; } for(i=1;i<=n;i++) { dp[i][0]=inf; s[i][0]=0; } for(j=1;j<=p;j++) { s[n+1][j]=n-1; for(i=n;i>=1;i--) { dp[i][j]=inf; for(k=s[i][j-1];k<=s[i+1][j];k++) { if(dp[k][j-1]+getw(k+1,i)<dp[i][j]) { dp[i][j]=dp[k][j-1]+getw(k+1,i); s[i][j]=k; } } } } printf("%d\n",dp[n][p]); } return 0; }