士兵排队(分治法)

在一个划分成网格的操场上,n个士兵散乱地站在网格点上。网格点用整数坐标(x,y)表示。士兵们可以沿网格边往上、下、左、右移动一步,但在同一时刻任一网格点上只能有一名士兵。按照军官的命令,士兵们要整齐地列成一个水平队列,即排列成(x,y),(x+1,y),…,(x+n-1,y)。如何选择x和y的值才能使士兵们以最少的总移动步数排成一行。

编程计算使所有士兵排成一行需要的最少移动步数。

题目引自POJ

输入格式:
第1行是士兵数n,1≤n≤10000。接下来n行是士兵的初始位置,每行有2个整数x和y,-10000≤x,y≤10000。

输出格式:
一个数据,即士兵排成一行需要的最少移动步数。

输入样例:
5
1 2
2 2
1 3
3 -2
3 3

输出样例:
8

士兵排对分为x方向和y方向 所以我们可以把x和y分开来思考 类似与输油管道问题

对于Y方向

设n个士兵的Y轴坐标分别为:Y1,Y2 …… …… Yn, 他们最后站在同一行上,设目标坐标为Y0,
则n个士兵最终在Y轴的需要移动的总的步数值为S1:
S1=|Y1-Y0|+|Y2-Y0|+ …… …… +|Yn-Y0|
结论:Y0取所有Yi的中间值时可以使得S1达到最小(这个结论可以证明)

对所有的Y轴坐标进行排序(O(nlogn))或者进行线性时间选择(O(n))然后取“中间”点的Y轴坐标值作为最佳位置Y0的值

对于X方向

(1)首先需要对所有士兵的X轴坐标值进行排序(为了方便就近移动)
(2)然后,按从左至右的顺序依次求出每个士兵所对应的“最终位置”(最优),所移动的步数总和就是X轴方向上需要移动的步数
设排序后n个士兵在X轴坐标为: X1’,X2’ …… …… Xn’
他们最终位置”的X轴坐标值为:X0,X0+1,X0+2 …… …… X0+(n-1)
则n个士兵最终X轴的需要移动的总的步数值为S2:
S2=|X1’-X0| + |X2’-(X0+1)|+… +|Xn’-(X0+n-1)|
经过变换
S2=|X1’-X0|+ |(X2’-1)-X0|+ …+|(Xn’-(n-1))-X0|
注意到公式的形式与Y轴方向上的考虑一样,同样是n个已知数分别减去一个待定数后取绝对值,然后求和
结论:求出x1’, x2’-1,… Xn’-(n-1) 的中位数,即求得X0值,最后算出最优解。

采用二分排序(快排)

#include<stdio.h>
#include<stdlib.h>
typedef struct loc{
	int *x;
	int *y;
}Coordinate;
void sort(int a[],int left,int right){
	int i,j,temp,t;
	if(left>right){
		return ;
	}
	temp = a[left];
	i  = left;
	j = right;
	while(i!=j){
		while(a[j]>=temp&&i<j)
			j--;
		while(a[i]<=temp&&i<j)
			i++;
		if(i<j){
			t = a[i];
			a[i] = a[j];
			a[j] = t;
		}
	}
	a[left] = a[i];
	a[i] = temp;
	sort(a,left,i-1);
	sort(a,i+1,right);
}
int main(){
	int i,n,x;
	scanf("%d",&n);
	Coordinate p;
	p.x = (int *)malloc(sizeof(int)*n);
	p.y = (int *)malloc(sizeof(int)*n);
	for(i=0;i<n;i++){
		scanf("%d %d",&(p.x[i]),&(p.y[i]));
	}
	sort(p.y,0,n-1);
	int sum=0;
	x = p.y[n/2];
	for(i=0;i<n;i++){
		sum+=abs(p.y[i]-x);
	}
	sort(p.x,0,n-1);
	for(i=0;i<n;i++){
		p.x[i] = p.x[i]-i;
	}
	sort(p.x,0,n-1);
	x = p.x[n/2];
	for(i=0;i<n;i++){
		sum+=abs(p.x[i]-x);
	}
	printf("%d",sum);
}
士兵排队(分治法)士兵排队(分治法) 进击的猴子wa 发布了63 篇原创文章 · 获赞 18 · 访问量 1643 私信 关注
上一篇:数学


下一篇:求两个球面坐标点(经纬度)之间的距离