POJ1723士兵站队问题

题目简述: POJ1723,N名士兵分散在不同位置,各自具有不相同的坐标{x,y},士兵每次可以沿x或y轴移动一步,若命令士兵沿平行x轴的方向排成一列,且移动过程中士兵位置不可重合,求N个士兵移动总数的最小值。
输入要求 先输入一个总数N, 后面输入N组xy坐标,如:
5
1 2
2 2
1 3
3 -2
3 3
输出要求 只需要输出总步数即可 如 : 8
题目分析
首先,面对这个问题,我们有如下三点疑问:
1、如何确定队列的具体位置
2、如何计算各个士兵移动的步数
3、如何保障移动过程中士兵不可重合
对于这三个疑问来看,确定了1我们才可以进一步去思索2,思索好了2,又要思索如何避免3,但实际上,N个士兵是可以凭借适当的移动方向和顺序来规避位置重合的情况发生,所以这里对于3的疑问大可以扔掉。
而抛开3的顾虑之后,我们已经不用纠结具体个人的移动,甚至可以拆分每个人的XY坐标,进行不同的讨论,所以对于2,我们可以分别求士兵到队列对应位置的X距离之和与Y距离之和,因一次移动距离为1,所以我们可以直接以距离代替步数。
而后,对于1问题,我们自然是优先考虑队列的y轴最优坐标,这里设设之为Y0,N个士兵各自的Y轴坐标我由小到大依次设为Y1,Y2,…,YN。
而各个士兵距离Y0线的垂直距离的和:S=|Y1-Y0|+|Y2-Y0|…+|YN-Y0|;
若欲有最短路径,也即S最小,则Y0为{Y1,Y2,Y3…Yn}的中位数,而非平均数,下图有证
在这里插入图片描述
如上图,中位数永远是所有数据内最中心位置的数,
于是恒有S=|Y1-Y0|+|Y2-Y0|…+|YN-Y0|=|Y1-Y0|+|YN-Y0|+|Y2-Y0|+|Y3-Y0|=|YN-Y1|+|Y3-Y2|
而相反,我们无法保证平均数一定在每对数据的中心,
如上图,平均数距离Y2,Y3的S为(15-5)+(15-3)=2*(15-5)+(5-3)=22。而如果取用中位数替代则S只为2.
了解到这一点,由于其他Y轴坐标都是已知数据,我们就可以确定Y0的大小。
而后,我们又要展开对队列X轴坐标的讨论,这里我们且设,X1到XN由小到大排序,队列最左端X轴坐标为X0,最右端为X0+n-1。
同上,
S=|X1-X0|+|X2-X0-1|+|X3-X0-2|+…+|XN-X0-N+1|
=|(X1)-X0|+|(X2-1)-X0|+|(X3-2)-X0|+…+|(XN-N+1)-X0|
此时,N与X1到XN的各个x坐标皆为已知数据,X0为未知数据,
欲求S最小,则同上X0为{X1,X2-1,X3-2,…,XN-N+1}的中位数
而中位数的求和在我国的小学教育里就已经全民普及,这里不做陈述。
分别求出
Sx与Sy,其和就为总步数,最后上代码(我写的有点繁琐)

#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
struct pos{
	int x,y;
};
pos sol[10009];
bool rowy(pos a,pos b)
{
	return a.y<b.y;
}
bool rowx(pos a,pos b)
{
	return a.x<b.x;
}
int main()
{
	int num,i,line_x,line_y;
	int step=0;
	cin>>num;
	for(i=1;i<=num;i++)
	cin>>sol[i].x>>sol[i].y;
	sort(sol+1,sol+num+1,rowy);
	if(num%2==1)
	line_y=sol[(num+1)/2].y;
	else
	line_y=(sol[num/2].y+sol[num/2+1].y)/2;
	for(i=1;i<=num;i++)
	step+=abs(sol[i].y-line_y);	
	sort(sol+1,sol+num+1,rowx);
	int ad=0;
	for(i=1;i<=num;i++)
	{
		sol[i].x-=ad;
		ad++;
	}
	sort(sol+1,sol+num+1,rowx);
	if(num%2==1)
	line_x=sol[(num+1)/2].x;
	else
	line_x=(sol[num/2].x+sol[num/2+1].x)/2;
	for(i=1;i<=num;i++)
	step+=abs(sol[i].x-line_x);	
	printf("%d",step);
	return 0;
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值