邮局post office(dp优化之四边形不等式)

本文介绍了一个经典的邮局选址问题,该问题是寻找最佳位置以最小化各个村庄到最近邮局的距离总和。文章提供了详细的算法思路,包括朴素的区间动态规划方法及优化后的动态规划策略,以实现高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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.

邮局(IOI2000)

Time Limit:10000MS  Memory Limit:65536K

Description

[问题描述] 
  一些村庄被建在一条笔直的高速公路边上,我们用一条坐标来描述这条高速,每一村庄的坐标都是整数,没有两个村庄坐标相同,两个村庄间的距离,定义为它们坐标值差的绝对值。 
  我们需要在一些村庄建立邮局,当然,并不是每一个村庄都必须建立邮局。邮局必须被建在村庄里,因此它的坐标和它所在的村庄坐标相同。每个村庄使用离它最近的那个邮局,建立这些邮局的原则是:所有村庄到各自使用的邮局的距离总和最小。 
你的任务是编写一程序,在给定了每个村庄的坐标和将要建立的邮局数之后,按照上述原则,合理地选择这些邮局的位置。 

Input

  文件的第一行包含两个整数:第一个整数是村庄的数目V,1 < V < 300;第二个整数是将建立的邮局数P,1 < P < 30 且 P < V。 
  文件的第二行按照递增顺序列出了V个整数。这V个整数分别表示了各村庄的位置坐标。对于每一个位置坐标X,1 < X < 10000。 

Output

  一行,一个整数S,表示你所求出的所有村庄到离它最近邮局的距离总和。 

Sample Input

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

Sample Output

9


朴素的区间dp就可以解决这道题了,容易看出dp方程为:

dp[i][j]=min(dp[i][j],dp[k][j-1]+w[k+1][i]);

其中dp[i][j]表示前i个村庄建j个邮局的最小距离和,k枚举1到i的所有村庄;w[k+1][i]表示第k+1个村庄到第i个村庄建一个邮局的最小距离和,有一个显然的性质:在某一段区间上建一个邮局,最小距离和为在其中点村庄上建。因此n^3可以预处理出w数组,dp本身三重循环也是n^3,总时间复杂度为O(n^3)。

下面我们开始说一个更优的dp:

用s[i][j]表示dp[i][j]的决策变量,即在dp方程dp[i][j]=min(dp[i][j],dp[k][j-1]+w[k+1][i])中,dp[s[i][j]][j-1]+w[s[i][j]+1][i]最优;我们可以发现对于任意区间[i,j]含于[i',j'](即区间[i',j']包含[i,j]),总有w[i][j]<w[i'][j'],我们称它符合区间单调性,在满足w的区间单调性情况下,如果w也满足四边形不等式,那么决策变量s也符合区间单调性(详细证明大家另找资料)。

整理一下,如果一个状态转移方程可以写成类似于dp[i][j]=dp[s[i][j]][j-1]+f(s[i][j])的形式,那么只要f满足区间单调性与四边形不等式,决策变量s也符合区间单调性。四边形不等式就是通过证明f(i,j)+f(i',j')<=f(i',j)+f(i,j'),i<=i'<=j<=j',从而证明决策变量满足区间单调性。

那么,我们在第三重循环枚举k时就不需要从1枚举到i了,由区间单调性知:s[i][j]一定在s[i-1][j]到s[i][j+1]、或s[i][j-1]到s[i+1][j]之间,我们只需要在这之间枚举,更新s[i][j]和dp[i][j]即可。

但是对于w的预处理我们也要优化,否则时间复杂度还是O(n^3),w[i][j]是在第i到第j个村庄建一个邮局的最小距离和,前面提到在中点村庄上建,可以推出如下式子w[i][j]=w[i][j-1]+abs(x[i]-x[(i+j)/2].

考虑两种情况,一种是(i+j-1) mod 2==0,这时(i+j)/2==(i+j-1)/2,故建邮局的村庄不变,只要把新村庄到原邮局的距离加进w[i][j]即可;

对于(i+j-1) mod 2==1时,新邮局要在原邮局位置往右挪一位,举个例子:

1   2   3   4   5   6   7

i         L   N             j

(其中L表示原邮局,N表示新邮局)

我们发现移动邮局后,对于村庄1、2、3,距离都增加了x[4]-x[3],而对于区间4、5、6,距离都减少了x[4]-x[3],所以对于前i到j-1个邮局,距离和是不变的,只要把新村庄到新邮局的距离加进w[i][j]即可。

代码如下:

#include<cstdio>
#include<cstring>
using namespace std;
	int f[301][31];
	int w[301][301];
	int s[301][301];
	int x[301];
	int v,p;
int abs(int x)
{
	if (x<0) return (-x);
	return x;
}
int main()
{
	scanf("%d%d",&v,&p);
	for (int i=1;i<=v;i++)
		scanf("%d",&x[i]);
	for (int i=1;i<=v;i++)
		for (int j=i+1;j<=v;j++)
		{
			int mid=(i+j)/2;
			w[i][j]+=w[i][j-1]+abs(x[j]-x[mid]); //预处理w数组
		}
	int INF=2147483647/3;
	for (int i=1;i<=v;i++) //初始化f[i][1]与s[i][1]
	{
		f[i][1]=w[1][i];
		s[i][1]=1;
	}
	for (int j=2;j<=p;j++) //因为f[i][1]已初始化,j从2开始枚举
	{
		s[v+1][j]=v; //限定最大区间
		for (int i=v;i>j;i--) //注意要倒着枚举
		{
			f[i][j]=INF;
			for (int k=s[i][j-1];k<=s[i+1][j];k++)
				if (f[i][j]>f[k][j-1]+w[k+1][i])
				{
					f[i][j]=f[k][j-1]+w[k+1][i];
					s[i][j]=k;
				}
		}
	}
	printf("%d",f[v][p]);
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值