由于水平原因,博客大部分内容摘抄于网络,如有错误或者侵权请指出,本人将尽快修改

AcWing 104. 货仓选址

AcWing 104. 货仓选址

一、题目描述

1. 问题核心

在一条数轴上有N家商店,各自的坐标为A₁∼Aₙ。需要在数轴上选择一个位置建立货仓,使得货仓到所有商店的距离之和最小,最终输出这个最小距离和。

2. 输入输出格式

  • 输入:第一行输入整数N(商店数量),第二行输入N个整数(各商店的坐标)。
  • 输出:一个整数,表示货仓到所有商店的最小距离之和。

3. 数据范围与样例

  • 数据范围:1≤N≤100000,0≤Aᵢ≤40000。
  • 输入样例:
4
6 2 9 1
  • 输出样例:12

二、解题思路

1. 核心问题:为什么是中位数?

货仓选址的关键是找到一个位置,使得所有点到该位置的距离之和最小。这一问题的最优解并非平均数,而是中位数,具体推导如下:

假设货仓初始位于最优位置,此时:

  • 货仓左侧有a家商店,右侧有n-a家商店。
  • 左侧商店到货仓的距离和为p,右侧为q,总距离和p+q为最小值。

当货仓向左移动x个单位时,新的总距离和为:

p - a·x + q + (n-a)·x = p+q + (n-2a)·x

由于初始位置是最优解,移动后的总距离和不能减小,因此(n-2a)·x≥0(x>0)。当a=n/2时,n-2a=0,移动货仓不会改变总距离和,此时位置即为中位数,是最优解。

2. 中位数的取值规则

  • 若数组下标从0开始,中位数为排序后数组的a[n/2]。
  • 若数组下标从1开始,中位数为排序后数组的a[n/2 + 1]。
  • 补充说明:中位数所在的“中间范围”内,任意位置的总距离和均相同,无需严格局限于某个点。

三、代码实现

1. 代码整体框架

解题步骤可简化为:输入数据→排序数组→计算各点到中位数的距离和→输出结果。具体代码如下:

   public static int minDistance(int[] arr, int n){
        Arrays.sort(arr);
        int p = arr[n/2];
        int total =0;
        for (int e:arr){
            total+=Math.abs(e-p);
        }
        return total;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] tokens = br.readLine().split(" ");
        int[] a = new int[n];
        for (int i = 0; i < n; i++) {
            a[i] = Integer.parseInt(tokens[i]);
        }
        System.out.println(minDistance(a,a.length));
    }

2. 关键代码解释

  • sort:对坐标数组排序是获取中位数的前提,排序后数组按从小到大排列,时间复杂度O(nlogn),适配N=1e5的规模。
  • abs(a[i] - a[n / 2]):计算每个坐标与中位数的距离绝对值,累加后得到最小距离和,时间复杂度O(n)。

四、总结

货仓选址问题的核心是利用“中位数最小化距离和”的数学性质,解题流程简洁高效:排序数组后取中位数,再计算各点到中位数的距离和。该解法时间复杂度为O(nlogn)。

车辆移动到连续位置:最小总步数问题详解

在直线坐标上的优化问题中,“车辆移动到连续位置”是经典题型。本文将从题目分析、思路推导到代码实现,完整拆解这类问题的解题逻辑,帮助你掌握核心解法。

一、题目描述

1. 问题核心

给定 N 辆车在无限长直线上的整数坐标(可负,绝对值最大达 2×10⁹),需将所有车移动到 连续的整数位置(形如 x, x+1, x+2, ..., x+N-1)。每次移动一辆车一步(距离为 1),求最少需要的总步数。

2. 关键约束

  • 车辆初始位置:整数坐标,可正可负,范围无上限(仅绝对值≤2×10⁹)。
  • 目标状态:所有车的位置为连续整数序列,长度为 N。
  • 优化目标:总移动步数最小(每步计 1 次)。

二、解题思路

1. 关键观察

最终目标是让车辆分布在长度为 N 的连续整数区间内,核心是找到合适的起始点 x,使所有车移动到目标序列 [x, x+1, ..., x+N-1] 的总步数最小。

2. 数学转化(核心步骤)

  • 第一步:对原始车辆位置数组 positions 排序,得到有序数组 a[0], a[1], ..., a[N-1]
  • 第二步:定义目标序列 t[i] = x + i(i 从 0 到 N-1),其中 x 是目标序列的起始点。
  • 第三步:总移动步数为各车辆初始位置与目标位置的距离绝对值之和,即:
    总步数 = Σ|a[i] - t[i]| = Σ|a[i] - (x + i)|
    
  • 第四步:简化表达式,令 b[i] = a[i] - i,则总步数转化为:
    总步数 = Σ|b[i] - x|
    
  • 第五步:根据数学性质,当 x 取 b 数组的 中位数 时,绝对值之和最小,这是该问题的最优解。

3. 解题流程总结

  1. 对原始位置数组排序。
  2. 计算辅助数组 b[i] = 排序后的位置[i] - i
  3. 找到 b 数组的中位数,作为最优起始点 x。
  4. 计算所有 |b[i] - x| 的和,即为最小总步数。

三、代码实现与分析

1. 正确代码(Java 版)

    private static long minMoveSteps(long[] positions) {
        int n = positions.length;
        // 创建 b[i] = positions[i] - i
        long[] b = new long[n];
        for (int i = 0; i < n; i++) {
            b[i] = positions[i] - i;
        }
        Arrays.sort(b);
        long x = b[n / 2]; // 中位数
        long total = 0;
        for (int i = 0; i < n; i++) {
            total += Math.abs(b[i] - x);
        }
        return total;
    }

2. 代码关键解析

  • 排序操作:对原始位置数组排序是后续计算 b 数组的基础,确保辅助数组的规律性,时间复杂度 O(NlogN)。
  • 辅助数组 b:通过 positions[i] - i 转化,将“连续位置”的约束转化为“找中位数最小化绝对值和”的经典问题,降低解题难度。
  • 中位数取值:b[n/2] 是数组排序后的下中位数,对于奇偶长度的数组均适用,能保证绝对值和最小。
  • 总步数计算:通过 Math.abs(positions[i] - (x + i)) 还原每个车辆的实际移动距离,累加得到结果,时间复杂度 O(N)。
posted @ 2025-11-22 17:46  小纸条  阅读(3)  评论(0)    收藏  举报