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. 解题流程总结
- 对原始位置数组排序。
- 计算辅助数组
b[i] = 排序后的位置[i] - i。 - 找到
b数组的中位数,作为最优起始点 x。 - 计算所有
|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)。
加油啦!加油鸭,冲鸭!!!

浙公网安备 33010602011771号