题目概述
在一个划分成网格的操场上, n个士兵散乱地站在网格点上。由整数 坐标 (x,y) 表示。士兵们可以沿网格边上、下左右移动一步,但在同时刻任一网格点上只能有名士兵。按照军官的命令,们要整齐地列成个水平队列,即排成 队列,即排成 (x,y),(x+1,y), …,(x+n -1,y) 。如何选择 x 和y的值才能使 士兵们以最少的总移动步数排成一列。
解题思路
虽然士兵的坐标分横纵,但仔细想想,其实可以分开来算。
我们可以先排序,并求出y轴坐标的中位数。
对于x轴,可以先假设将士兵们排在x=0,1,2…(n-1)的位置(按横坐标顺序排列),记录下每个士兵需要移动的长度, 排序,再找到它们的中位数cmid。则x轴上需要移动的总长度为Sx=|x1-cmid|+|x2-cmid+1|+…+|xn-cmid+n-1|。
对于y轴,想要排在同一行,设该行纵坐标为p,则y轴上需要移动的总长度为Sy=|p-y1|+|p-y2|+…+|p-yn|。当p为所有x轴坐标的中位数时,总长度最小。
最后将Sx与Sy相加,输出结果。
时间复杂度:O(nlog n)
空间复杂度:O(n)
源程序
var
a,b,c,d:array[1..10002]of longint;
i,n,t,ans,mid:longint;
procedure ms(s,m,t:longint);
var
f:array[1..10002]of longint;
i,j,k,p:longint;
begin
i:=s;j:=m+1;k:=s-1;
while (i<=m)and(j<=t)do
begin
if a[i]<a[j] then begin
inc(k);
f[k]:=a[i];
inc(i);
end
else begin
inc(k);
f[k]:=a[j];
inc(j);
end;
end;
while i<=m do begin inc(k);f[k]:=a[i];inc(i);end;
while j<=t do begin inc(k);f[k]:=a[j];inc(j);end;
for p:=s to t do
a[p]:=f[p];
end;
procedure ms2(l,r:longint);
var
m:longint;
begin
if l>=r then exit;
m:=(l+r) div 2;
ms2(l,m);
ms2(m+1,r);
ms(l,m,r);
end;
begin
readln(n);
for i:=1 to n do
readln(b[i],a[i]);
ms2(1,n);
ans:=0;
mid:=a[(n div 2)+1];
for i:=1 to n do
ans:=ans+abs(a[i]-mid);
a:=b;
ms2(1,n);
b:=a;
for i:=1 to n do
c[i]:=b[i]-i+1;
a:=c;
ms2(1,n);
c:=a;
mid:=c[(n div 2)+1];
for i:=1 to n do
ans:=ans+abs(b[i]-mid-i+1);
write(ans);
end.