题目描述
继在鹅厂工作的DJ训练完鹅厂的企鹅们之后,DJ发明了一个新游戏。该游戏在n*n的棋盘上进行,其中恰好有n个企鹅,企鹅向四个方向之一移动一格算作一步。DJ希望用最少的总步数把这些企鹅变成一排,即所有企鹅都在同一行或同一列(由于DJ目不斜视,他认为所有企鹅在同一对角线上的情况不算一列)。 企鹅们为了避免DJ的毒打需要尽快排成一排,你能帮可怜的小企鹅们解决这个问题吗。注意任意时刻不能有两个企鹅在同一格。
输入
第一行一个整数n(n<=6*10^5)表明棋盘大小。 接下来n行,每行两个整数Xi,Yi(1<=Xi,Yi<=n),表示第i只企鹅的坐标。保证初始 企鹅的坐标互不相同。
输出
一行一个整数表示最少需要的步数。
样例输入
5
1 2
2 4
3 4
5 1
5 3
样例输出
6
思路
中位数性质!最优的就是移动到左右尽可能相等的位置
左边a个企鹅,右边b个企鹅,移动到x=c的位置,当a>b时,c往左移动更优,因为c左边的企鹅都少走一步,c右边的企鹅多走一步,因为a>b所以总步数更少......
AC代码
#include <iostream>
#include <stdio.h>
#include <queue>
#include <set>
#include <string>
#include <map>
#include <cmath>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int maxn = 6e5 + 5;
const int inf = 0x3f3f3f3f;
int x[maxn], y[maxn];
ll numx[maxn], numy[maxn];
ll sumx[maxn], sumy[maxn];
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; ++i)
scanf("%d %d", &x[i], &y[i]);
sort(x + 1, x + n + 1);
sort(y + 1, y + n + 1);
ll now = 1;
for (int i = 1; i <= n; ++i)
{
sumx[i] = sumx[i - 1];
numx[i] = numx[i - 1];
while (x[now] == i)
{
sumx[i] += i;
++numx[i];
++now;
}
}
now = 1;
for (int i = 1; i <= n; ++i)
{
sumy[i] = sumy[i - 1];
numy[i] = numy[i - 1];
while (y[now] == i)
{
sumy[i] += i;
++numy[i];
++now;
}
}
ll pjx = 1, pjy = 1;
for (int i = 1; i <= n; ++i)
if (numx[i] > numx[n] / 2)
{
pjx = i;
break;
}
for (int i = 1; i <= n; ++i)
if (numy[i] > numy[n] / 2)
{
pjy = i;
break;
}
ll ansx1 = 0, ansy1 = 0, ansx = 0, ansy = 0;
ansy = numy[pjy - 1] * pjy - sumy[pjy - 1];
ansy += (sumy[n] - sumy[pjy]) - (numy[n] - numy[pjy]) * pjy;
ansx = numx[pjx - 1] * pjx - sumx[pjx - 1];
ansx += (sumx[n] - sumx[pjx]) - (numx[n] - numx[pjx]) * pjx;
for (int i = 1; i <= n; ++i) //转移需要的步数
{
ansx1 += abs(i - x[i]);
ansy1 += abs(i - y[i]);
}
ll ans = min(ansx1 + ansy, ansy1 + ansx);
printf("%lld\n", ans);
return 0;
}