题目大意
一些士兵站在矩阵的一些方格内,现要把他们移动到一横排,并连续地排成一队,问最少需 要移动多少步。
思路
先来考虑一个经典的用中位数求解的问题:
数轴上n个点,将他们移到一个相同位置的最小移动距离是多少?
写成表达式就是求: ,其中a是变化量。当 n 为奇数时,a只能是取最中间那个点的位置(假设取最中间那个点右边相距为 d 的位置,那么有个点的花费增加d,有个点花费减少d,整体花费增加);n为偶数时,a 可以取中间两个点之间的任何点(包括两端点),证明方法类似。
这个问题中稍有不同,y方向需要移到同一高度,与上述是一样的,但还需保证x方向连续排列。我们发现,x和y方向的移动互不干扰,可以分开考虑,最终结果是两方向上移动的最小代价直接相加。
y方向移到某一高度直接用上面的中位数方法求解即可。对于x方向,我们先将点按x坐标(升序)排序,假设最终要移到的连续位置的横坐标值为到,是一个公差为1的等差数列。现在就是要把每个点一一对应移到这些位置上,如何一一对应?感觉上,应该是按顺序一一对应,即移到。确实如此,假设先按这种方案移动,使其中两个点的对应的点交换,那么一定会在中间产生一段交叠区域,即多余花费。现在,我们要求的即为的最小值,为了向中位数方法的方向靠近,我们在每个绝对值里提出一个相同的值(因为在可以用中位数方法的问题中,每个绝对值里的被减数是相同的),这里就取,化为。由此,我们只需要将每个点的x坐标依次减去0...n-1,就可以用取中位数的方法求解。
AC代码
#include<iostream>
#include<algorithm>
using std::cin;
using std::cout;
using std::sort;
long long abs(long long a){
return (a > 0) ? a : -a;
}
int main(){
long long int x[10000], y[10000];
int n;
long long int ans = 0;
cin>>n;
for(int i = 0; i < n; i++){
cin>>x[i]>>y[i];
}
sort(y, y + n);
for(int i = 0; i < n; i++){
ans += abs(y[i] - y[n>>1]);
}
sort(x, x + n);
for(int i = 0; i < n; i++){
x[i] -= (long long )i;
}
sort(x, x + n);
for(int i = 0; i < n; i++){
ans += abs(x[i] - x[n>>1]);
}
cout<<ans;
return 0;
}