Loading

洛谷 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;
}
posted @ 2020-12-31 23:08  zhb2000  阅读(237)  评论(0编辑  收藏  举报