题解:洛谷 P10452 货仓选址

【题目来源】

洛谷:P10452 货仓选址 - 洛谷

【题目描述】

在一条数轴上有 \(N\) 家商店,它们的坐标分别为 \(A_1 \sim A_N\)

现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。

为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。

【输入】

第一行输入整数 \(N\)

第二行 \(N\) 个整数 \(A_1 \sim A_N\)

【输出】

输出一个整数,表示距离之和的最小值。

【输入样例】

4
6 2 9 1

【输出样例】

12

【核心思想】

  1. 问题分析:在一条数轴上有 \(N\) 家商店,坐标分别为 \(A_1 \sim A_N\)。需要选择一个位置建立货仓,使得货仓到所有商店的距离之和最小。本质是在一维数轴上寻找一个点,使其到所有给定点的绝对距离之和最小,这是一个经典的数学优化问题,其最优解是所有坐标的中位数。

  2. 算法选择

    • 排序 + 中位数:先将所有商店坐标从小到大排序。若 \(N\) 为奇数,货仓建在最中间的那个坐标上;若 \(N\) 为偶数,建在中间两个坐标之间的任意位置均可。最常用的做法是取下标 N/2 处的元素作为货仓位置。
    • 贪心策略:根据绝对值不等式 \(|a-x| + |b-x| \ge |a-b|\),等号成立当且仅当 \(x\)\(a\)\(b\) 之间。将商店两两配对(最外层的 \(A_1\)\(A_N\)\(A_2\)\(A_{N-1}\)……),要使每一对的距离和最小,\(x\) 必须落在每一对的内侧区间内。这些区间的公共交集即为中位数。
  3. 关键步骤

    • 读入数据:读取整数 \(N\),以及 \(N\) 个商店坐标 \(A_1 \sim A_N\)
    • 排序:将坐标数组按升序排列。
    • 确定货仓位置:取排序后数组的中位数,即 \(A_{N/2}\)(下标从 0 开始)。
    • 计算最小距离和:遍历所有坐标,累加每个坐标与中位数的绝对差值。
    • 输出答案:输出累加得到的距离之和。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N \log N)\),主要来自排序操作。
    • 空间复杂度:\(O(N)\),用于存储商店坐标数组。
  5. 贪心 + 中位数的核心思想

    • 绝对值不等式\(|a-x| + |b-x| \ge |a-b|\),当且仅当 \(x\)\(a\)\(b\) 之间时取等号。将商店坐标两两配对,最优的 \(x\) 必须同时落在每一对的内侧,即所有区间的公共交集中。
    • 中位数的决定性:排序后,中位数恰好是这些嵌套区间的唯一交点(奇数个点)或交集区间(偶数个点)。货仓建在中位数处,可以保证到所有商店的绝对距离之和最小。
    • 平均数 vs 中位数:平均数最小化的是距离的平方和,而中位数最小化的是绝对距离之和。本题要求的是绝对距离之和,因此应选择中位数而非平均数。
    • 适用场景:适用于一维数轴上的选址问题,目标是最小化到所有点的绝对距离之和,核心是找到数据的中位数。

【算法标签】

普及- #贪心

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 100005;  // 定义数组最大长度
int n;                     // 数字个数
int a[MAX_N];              // 存储数字的数组
int ans;                   // 存储最终结果(总距离和)

int main()
{
    // 输入数字个数
    cin >> n;

    // 输入所有数字
    for (int i = 0; i < n; i++)
        cin >> a[i];

    // 对数组进行排序(升序)
    sort(a, a + n);

    // 计算所有数字到中位数的距离之和
    for (int i = 0; i < n; i++)
        ans += abs(a[i] - a[n / 2]);  // 累加每个数字与中位数的绝对差

    // 输出结果
    cout << ans;

    return 0;
}
// 按照起始下标从1开始重新写一遍
#include <bits/stdc++.h>
using namespace std;

const int MAX_N = 100005;  // 定义数组最大长度
int n;                     // 数字个数
int a[MAX_N];              // 存储数字的数组(从1开始索引)
int ans;                   // 存储最终结果(总距离和)

int main()
{
    // 输入数字个数
    cin >> n;

    // 输入所有数字(从1开始存储)
    for (int i = 1; i <= n; i++)
        cin >> a[i];

    // 对数组进行排序(升序,从a[1]到a[n])
    sort(a + 1, a + n + 1);

    // 计算所有数字到中位数的距离之和
    for (int i = 1; i <= n; i++)
        ans += abs(a[i] - a[(n + 1) / 2]);  // 累加每个数字与中位数的绝对差

    // 输出结果
    cout << ans;

    return 0;
}

【运行结果】

4
6 2 9 1
12
posted @ 2026-09-02 22:18  团爸讲算法  阅读(15)  评论(0)    收藏  举报