You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
52 lines
2.0 KiB
52 lines
2.0 KiB
## [洛谷 $P1889$ 士兵站队](https://www.luogu.com.cn/problem/P1889)
|
|
|
|

|
|
|
|
|
|
### 问题简述
|
|
这道题我们可以换另一种思路去看待它,就容易理解了:
|
|
|
|
在一个平面上,把 $n$ 个点排列在一条与 $x$ 轴平行的直线的整点上,且相邻两点的距离为 $1$ 。
|
|
|
|
求一种排列方案,使得这$n$ 个点到目标位置的 **曼哈顿距离和最小**。
|
|
|
|
|
|
### 解法综述
|
|
由于是求曼哈顿距离,所以可以将 $x$ ,$y$ 分开考虑。
|
|
|
|
首先,要找到最优的一列(使移动步数最少的一列),其实就是要找一条平行于$x$轴的直线,设此直线为$y=k$,那么,每个点到这条线距离为$|y_i-k|$,不难发现,当$k$是所有点纵坐标的中位数时,距离之和最小。
|
|
|
|
找到了这条直线之后,又该把每个点移到哪个位置才能使结果最优呢?
|
|
|
|
可以设最左边的点(第一个点)移动后的位置为$b$,因为所有点必须排在一条线段上,那么第二个点的移动后的位置即为$b+1$,第三个移动后的位置为$b+2$......以此类推,第$n$个点移动后的位置为$b+n-1$。
|
|
|
|
那么横向移动的步数之和为$|x_0-b|+|x_1-b-1|+|x_2-b-2|+......+|x_{n-1}-b-n+1|$,所以,要使步数之和最小,只需要 **再找一次中位数** 即可。
|
|
|
|
|
|
### $Code$
|
|
```cpp {.line-numbers}
|
|
#include <bits/stdc++.h>
|
|
using namespace std;
|
|
const int N = 10010;
|
|
int x[N], y[N];
|
|
|
|
int n, res;
|
|
int main() {
|
|
cin >> n;
|
|
for (int i = 0; i < n; i++) cin >> x[i] >> y[i];
|
|
|
|
sort(y, y + n);
|
|
sort(x, x + n);
|
|
|
|
for (int i = 0; i < n; i++) res += abs(y[i] - y[n / 2]); // 对与x轴的一条平等线的最短距离和
|
|
|
|
for (int i = 0; i < n; i++) x[i] -= i;
|
|
|
|
sort(x, x + n);
|
|
for (int i = 0; i < n; i++) res += abs(x[i] - x[n / 2]);
|
|
|
|
// 输出
|
|
cout << res << endl;
|
|
return 0;
|
|
}
|
|
``` |