洛谷 P1862 输油管道问题
题意
题目链接:P1862 输油管道问题
不难看出每个油井的 \(x\) 坐标是没用的,所以问题转化为如下。
代数意义:给出 \(n\) 个数 \(y_1,y_2,\ldots,y_n\),找一个数 \(a\),使得 \(\sum_{i=1}^n |a-y_i|\) 最小。
几何意义:数轴上有 \(n\) 个点 \(y_1,y_2,\ldots,y_n\),在数轴上放置一个点 \(a\),使得线段 \(ay_1,ay_2,\ldots,ay_n\) 长度之和最小。
思路
为便于说明,假设 \(y_1,y_2,\ldots,y_n\) 从小到大有序。
如果从代数意义着手,你会发现式子里既有绝对值又有和式,很难找到思路,所以应该从几何意义着手。
当 \(n\) 为偶数时,\(a\) 放在最中间两个点之间是最优的,证明如下:首先只考虑 \(y_1\) 和 \(y_n\) 两个点,则点 \(a\) 应放在 \(y_1\) 和 \(y_n\) 中间。接着再把点 \(y_2\) 和点 \(y_{n-1}\) 纳入考虑,显然 \(a\) 应该放在 \(y_2\) 和 \(y_{n-1}\) 中间(此时 \(a\) 同样也在 \(y_1\) 和 \(y_n\) 中间)……依此类推即可得出结论。
当 \(n\) 为奇数时,\(a\) 放在中间那个点上是最优的,证明方法同上。
统一处理:取 \(y_{\lfloor\frac{n+1}{2}\rfloor}\) 作为 \(a\),或者取中位数也行。可以直接排个序取中间,也可以按快排的思想用分治法求,反正都能过。
代码
#include <cstdio>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 10000 + 5;
int arr[maxn];
int solve(int n)
{
sort(arr + 1, arr + 1 + n);
int a = arr[(n + 1) / 2];
int ans = 0;
for (int i = 1; i <= n; i++)
ans += abs(arr[i] - a);
return ans;
}
int main()
{
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
int x, y;
scanf("%d%d", &x, &y);
arr[i] = y;
}
printf("%d", solve(n));
return 0;
}