post office 动态规划题解

描述

There is a straight highway with villages alongside the highway. The highway is represented as an integer axis, and the position of each village is identified with a single integer coordinate. There are no two villages in the same position. The distance between two positions is the absolute value of the difference of their integer coordinates.

Post offices will be built in some, but not necessarily all of the villages. A village and the post office in it have the same position. For building the post offices, their positions should be chosen so that the total sum of all distances between each village and its nearest post office is minimum.

You are to write a program which, given the positions of the villages and the number of post offices, computes the least possible sum of all distances between each village and its nearest post office.

输入

Your program is to read from standard input. The first line contains two integers: the first is the number of villages V, 1 <= V <= 300, and the second is the number of post offices P, 1 <= P <= 30, P <= V. The second line contains V integers in increasing order. These V integers are the positions of the villages. For each position X it holds that 1 <= X <= 10000.

输出

The first line contains one integer S, which is the sum of all distances between each village and its nearest post office.

样例输入

10 5
1 2 3 6 7 9 11 22 44 50

样例输出

9

来源

IOI 2000

题目解析

大致意思是 n ,m 分别表示 n 个村庄,m 个邮局,怎么将邮局建在村庄中,才能让每个村庄去最近的邮局的和最小,并问这个最小的值是多少。

解题思路

我们先想最这个问题最简单的情况,设 m=1 也就是我们只建一个邮局,那么我们不难想到,在最中间找个地方建一定是最短的,然后我们推广到更多个邮局,也就是要找到很多个中间值,不妨我们先设

dis[i][j] 表示从 i 到 j 中间建一个邮局所用的最短距离

然后问题就来了,我们要怎么求 dis[i][j] 呢

现在我们假设 i 到 j-1 的中位数村庄修建了一个邮局位置为 k ,然后我们再新加入一个村庄 j 那么此时中位数村庄变成了 k' 

如果 i 到 j-1 之间有偶数个村庄,新加入村庄 j 后就变为奇数个村庄了(4/2=2,5/2≈2)

如果 i 到 j-1 之间有奇数个村庄,新加入村庄 j 后就变为偶数个村庄了,此时我们决定邮局建到中位数中较大的那个数,那中间值其实也不会改变((1+4)/2≈3,(1+5)/2=3)

那么 a[j] - a[(i + j) / 2] 就表示 j 到 新建邮局位置之间的距离,很巧妙的思路对吧

dis[i][j] = dis[i][j-1] + a[j] - a[(i + j) / 2]

解决完距离问题我们再想怎么推出动态转移方程呢

我们再设一个 dp[i][j] 表示在 i 个村庄中建了 j 个邮局的时候最小距离是多少

然后我们就可以推出动态转移方程

dp[i][j] = min(dp[i][j],dp[i][k]+dis[k+1][j])

此时我们需要注意一个细节,就是要提前弄好修建一个邮局的情况,这之后才能递推

代码示例

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;

int dp[1010][1010];// 表示 i 个村庄中修建 j 个邮局使得所有村庄到最近邮局的距离最近 
int dis[1010][1010];// 计算 i 到 j 村庄之间修建一个邮局,到这个邮局的最小距离 
int a[101000];
int n,m;

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<n;i++){
		for(int j=i+1;j<=n;j++){
			dis[i][j]=dis[i][j-1]+a[j]-a[(i+j)/2];
			//将邮局修建到这些村庄的中位数中 
		}
	} 
	for(int i=1;i<=n;i++) dp[i][1]=dis[1][i];//预先处理修建一个的情况 
	for(int i=2;i<=m;i++){
		for(int j=i;j<=n;j++){
			dp[j][i]=2147483647;//先赋值到最大然后找最小 
			for(int k=i-1;k<j;k++){
				dp[j][i]=min(dp[j][i],dp[k][i-1]+dis[k+1][j]);
			}
		}
	}
	cout<<dp[n][m]<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值