poj 1160 Post Office(四边形不等式优化dp)

转载自:http://www.cnblogs.com/staginner/archive/2012/03/12/2391925.html          

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;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值