给定N个坐标点,每个点代表一个士兵。现要让每个士兵都在一条水平线上,并且相邻。士兵每次可以上下左右移动一次。求达到目的的最少移动次数。任意时刻不能有两个士兵重叠。

此题可以分两步做:第一步求出所有点到水平线上的次数之和,再求出在水平线上重新排列需要的次数。

第一步很明显可以现找出要在哪条水平线上,找出一条水平线使得坐标点到线上距离之和最短,也就是要求Y轴坐标排序后的中位数那条。

第二步抽象一点,要发现所有士兵按X轴排序后的相对位置和排序前的相对位置是一样的,因为这样才能保证移动次数最少。可能你会担心有士兵在X轴上重叠了,但经过下面的步骤可以消除这一点。也就是不影响最后的答案。

假设最后开始坐标是a,也就是v[0]=a,v[1]=a+1......,所以就是要让

v[0]=>a

v[1]=>a+1

v[2]=>a+2

……

转换一下:

v[0]=>a

v[1]-1=>a

v[2]-2=>a

……

很惊奇的发现就是让v[0],v[1]-1,v[2]-2……的点到同一点a的距离,那么就很第一步的做法一样了,取中位数。

代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;

int n,ans,line[20005];
struct node 
{
    int a,b;
}v[10003];
bool cmpy(node a,node ab)
{
    if(a.b!=ab.b)
        return a.b<ab.b;
    return a.a<ab.a;
}
bool cmpx(node a,node ab)
{
    if(a.a!=ab.a)
        return a.a<ab.a;
    return a.b<ab.b;
}
int main(int argc, char **argv) {
    cin>>n;
    for(int i=0;i<n;++i)
        cin>>v[i].a>>v[i].b;
    sort(v,v+n,cmpy);
    int my=v[n/2].b;
    for(int i=0;i<n;++i){
        ans+=abs(my-v[i].b);
    }
    sort(v,v+n,cmpx);
    for(int i=0;i<n;++i)
        v[i].a-=i;
    sort(v,v+n,cmpx);
    int mx=v[n/2].a;
    for(int i=0;i<n;++i)
        ans+=abs(mx-v[i].a);
    cout<<ans<<endl;
    return 0;
}

 

posted on 2018-04-27 15:00  chagin  阅读(124)  评论(0)    收藏  举报