DP POJ1160

题意:

题目链接
  有一排村庄,坐标表示村庄的位置。如(1、4、6、10)表示:在这几个点有村庄。现在要在这一排村庄中建立邮局,使得其它村庄到他们最近邮局的距离总和最小。
其中邮局有V个(V<=300),邮局有P个(P<=30)。问怎么设置邮局位置,求出最小距离。

解法:

1.表示方法

  • dp[ i ][j] : 表示前 i 个村庄 建 j 个邮局时距离最小值。
  • sum [i][j] : 表示第 i 个村庄到第 j 个村庄建一个邮局的最小距离。

2. 递推公式的由来

这个题目有点类似于矩阵连乘问题。要求 dp[i][j] ,假设从 k 位置断开,前 k 个村庄建 j1 个邮局,后面 k+1 i 建一个邮局,然后求出断开位置使dp[i][j] 最小。所以递推关系式如下:

  • dp[i][j]=min(dp[i][j],dp[k][j1]+sum[k+1][i]) ;

3. sum[i][j] 的递推

i j之间建一个邮局,显然建在中位数的村庄位置时最小。因为题中村庄的位置已经从小到大排好序,因此只要求出中位数村庄即可(这里与村庄的位置大小无关)。

  • 如果村庄有奇数个,比如有三个村庄: x1,x2,x3 x1x2s1,x2x3s2 , 显然建在 x2 时距离最小为: s1+s2 。(可以自己造几个case看看).
  • 如果村庄个数为偶数个,那么建在中间两个位置的距离是一样的。比如: x1,x2,x3x4 x1x2s1,x2x3s2,x3x4s3 ,那么建在中间两个村庄的距离是一样的。建在 x2 s1+2s2+s3
    建在 x3 s1+2s2+s3 。 建在 x1 : 3s1+2s2+s3

  • 因此:在 i j之间建一个邮局的最小距离等于在 i j1之间建一个邮局的距离最小值加上 j 点到中位数村庄的距离。即:sum[i][j]=sum[i][j]+x[j]x[(i+j)/2]

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>

using namespace std;

int dp[300+1][300+1],sum[300+1][300+1];
int s[300+1];
int INF = 100000000;

int main()
{
    int V,P;
    scanf("%d%d",&V,&P);
    memset(dp,sizeof(dp),0);
    memset(sum,sizeof(sum),0);
    memset(s, sizeof(s), 0);
    for(int i = 1; i<=V; i++)
        scanf("%d",&s[i]);
    for(int i = 1; i<V; i++){
        for(int j = i+1; j<=V; j++)
            sum[i][j] = sum[i][j-1] + s[j] - s[(i+j)/2];
    }
    for(int i = 1; i<=V; i++)
        dp[i][1] = sum[1][i];
    for(int j = 2; j<=P; j++){
        for(int i = 1; i<=V; i++){
            dp[i][j] = INF;
            for(int k = 1; k<=i; k++)
                dp[i][j] = min(dp[i][j] ,dp[k][j-1]+sum[k+1][i]);
        }
    }
    printf("%d\n",dp[V][P]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值