POJ1723 LuoguP1889 SOLDIERS 士兵站队

这道题洛谷评分这么低我是没想到的。感觉这是目前我做得最巧妙的橙题了。

先简述一下题目:

平面内有随机分布的 \(N\) 个格点 \((x,y)\)点不可重合。每次操作可以选一个点朝 上/下/左/右 四个方向移动一格。问把这些点移动成一条连续的、平行于 \(x\) 轴的线段的最小操作数。


我们一步一步来解决这道题。

首先,我们把我们要描述的对象数学化。不妨设最终\(N\) 个点中最左边的点坐标为 \((X,Y)\),那么这些点就可以描述为 \((X,Y),(X+1,Y),(X+2,Y),\dots ,(X+N-1,Y)\)。首先我们可以发现,点 \((x,y)\) 纵向(平行于 \(y\) 轴)的运动距离最短是 \(|Y-y|\)

所以我们把纵向运动分离出来,用序列 \(A\) 记录每个点的纵坐标,得到问题:

  • 有一个序列 \(A=(A_1,A_2,\dots , A_N)\)。求 \(\min \limits_{Y\in\mathbb R}\Big\{{\sum \limits_{i=1}^{N}{|Y-A_i|} }\Big\}\)

这个问题不就是货仓选址(AcWing104)嘛!参考蓝书:

也有另一种证明,适合实数域,也更优美:(先把 \(A\) 由小到大排序,因为显然顺序不影响最终答案)

\[\min \limits_{Y\in\mathbb R}\Big\{{\sum \limits_{i=1}^{N}{|Y-A_i|} }\Big\}=\frac{\min \limits_{Y\in\mathbb R}\Big\{{2\cdot \sum \limits_{i=1}^{N}{|Y-A_i|}+ }\Big\}}{2} \]

即求下式的最小值:

\[S=\frac{(|Y-A_1|+|Y-A_N|)+(|Y-A_2|+|Y-A_{N-1}|)+\dots +(|Y-A_{N}|+|Y-A_{1}|)}{2} \]

利用绝对值三角不等式 \(|a|+|b|\geq |a\pm b|\) 得:

\[S\geq\frac{|A_1-A_N|+|A_2-A_{N-1}|+\dots +|A_N-A_1|}{2}=\left\{ \begin{aligned} &|A_1-A_N|+|A_2-A_{N-1}|+\dots +|A_{\frac N2}-A_{\frac N2 +1}|,& &2\ \Big|\ N,\\ &|A_1-A_N|+|A_2-A_{N-1}|+\dots +|A_{\frac {N+1}2-1}-A_{\frac {N+1}2 +1}|,& &otherwise. \\ \end{aligned} \right.\]

\(2\ \Big|\ N\) 时,\(Y\in [\frac N2,\frac N2 +1]\) 时取等;否则,\(Y=\frac {N+1}2\) 时取等。


现在我们来解决横向移动。这个麻烦很多。我们设一个大小为 \(N\) 的序列 \(A\) 记录 \(N\) 个点最终顺序下的原来的横坐标。注意,现在 \(A\) 是一个不确定的序列。

即,若点 \((x,y)\) 最终移到了 \((X+i,Y)\) 的位置,那么 \(A_i=x\)

那么最终横向的移动距离为:

\[Distance=\sum _{i=1}^{N}{|A_i-(X+i)|}=\sum _{i=1}^{N}{|(A_i-X)-i|} \]

显然因为 \(X\) 毕竟是常量(只是不确定),我们令序列 \(B=(A_1-X,A_2-X,\dots ,A_N-X)\),元素相对位置大小不改变,则 \(Distance=\sum\limits _{i=1}^{N}{|B_i-i|}\)

这里我们采用邻项交换的思想。如果我们交换 \(B_i\)\(B_{i+1}\), 那么最终移动距离分别为 \(|B_i-i|+|B_{i+1}-i-1|\)\(|B_{i+1}-i|+|B_i-i-1|\)

比较 \(|B_i-i|+|B_{i+1}-i-1|\)\(|B_{i+1}-i|+|B_i-i-1|\),只需比较 \(|B_i-i|-|B_i-i-1|\)\(|B_{i+1}-i|-|B_{i+1}-i-1|\)。设 \(f(x)=|x-i|+|x-i-1|,i\in [1,N]\),则其又可以表示为分段函数:

\[f(x)=\left\{ \begin{aligned} &-1,& &x<=i-1,\\ &2i+1-2x,& &i-1<x<=i, \\ &1,& &otherwise. \\ \end{aligned} \right. \]

如图是当 \(i=2.2\) 时的 \(f(x)\) 图象:

image

现在问题转化为了:当 \(f(B_i)\leq f(B_{i+1})\) 时,\(|B_i-i|+|B_{i+1}-i-1|\leq |B_{i+1}-i|+|B_i-i-1|\)

我们发现,\(f(x)\) 是一个单调不减函数。故 \(x_1\leq x_2\) 时,\(f(x_1)\leq f(x_2)\)。即 \(B_i\leq B_{i+1}\) 时比 \(B_i\geq B_{i+1}\) 时更优或等价。这就告诉我们,\(B\) 不下降,也就是 \(A\) 不下降时,原式取得最小

现在 \(A\) 成为一个确定的序列了,\(A_i\) 等于第 \(i\) 大的横坐标,那么 \(A_i-i\) 就是一个定值,我们设序列 \(C,C_i=A_i-i\)。我们再来试着求最小值。改写一下最终移动距离的公式。

\[Dis=\sum _{i=1}^{N}{|(A_i-i)-X|}=\sum _{i=1}^{N}{|C_i-X|} \]

它现在就变成了纵向移动的公式!现在会求了吧!就是要求:

\[\min \limits_{X\in\mathbb R}\Big\{{\sum \limits_{i=1}^{N}{|C_i-X|} }\Big\} \]

找序列 \(C\) 的中位数即可。


代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
int n,x[10055],y[10055],ans;
signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d%d",x+i,y+i);
	sort(x+1,x+1+n);
	//求得确切的序列 A
	for(int i=1;i<=n;i++)x[i]-=i;
	//现在 x 数组描述的就是序列 B
	sort(x+1,x+1+n);sort(y+1,y+1+n);
	for(int i=1;i<=n;i++)
		ans+=abs(x[i]-x[(n>>1)+1])+abs(y[i]-y[(n>>1)+1]);
	printf("%d",ans);
	return 0;
}
posted @ 2022-11-13 23:25  robinyqc  阅读(34)  评论(0)    收藏  举报