1. 程式人生 > >poj 1160 Post Office(四邊形不等式優化dp)

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)&lt;=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;
}