题解:学而思编程 货仓选址2
【题目来源】
学而思编程:货仓选址2
【题目描述】
在一条数轴上有 \(n\) 家商店,它们的坐标分别为 \(a_1\) 到 \(a_n\)
现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。根据运送的距离和商品的重量,所需的运费也不一样。向距离 \(d\) 公里的地方运送 \(W\) 公斤的商品所需的运费是 \(W×d\)。
运送到第 \(i\) 家商店的商品的重量是 \(w_i\)。
为了提高效率,求把货仓建在何处,可以使得运送所有商品所需运费之和最小。
【输入】
第一行输入整数 \(n\)。
第二行 \(n\) 个整数 \(a_1\) 到 \(a_n\)
第三行 \(n\) 个整数 \(w_1\) 到 \(w_n\)
【输出】
输出一个整数,表示运费之和的最小值。
【输入样例】
4
6 2 9 1
1 2 3 4
【输出样例】
29
【核心思想】
-
问题分析:给定 \(n\) 个商店在数轴上的坐标 \(a_1, a_2, \ldots, a_n\) 和对应的商品重量 \(w_1, w_2, \ldots, w_n\),需要选择一个位置建立货仓,使得运送所有商品所需运费之和最小。运费计算公式为 \(\sum_{i=1}^{n} w_i \times |a_i - x|\),其中 \(x\) 为货仓位置。这是一个加权中位数贪心问题,关键在于最优位置是加权中位数。
-
算法选择:
- 加权中位数性质:对于带权重的货仓选址问题,最优位置是加权中位数,即满足前缀加权和 \(\geq\) 总加权和的一半的位置
- 数学证明:设选址位置为 \(x\),总成本为 \(f(x) = \sum_{i=1}^{n} w_i \times |a_i - x|\)。当 \(x\) 为加权中位数时,\(f(x)\) 取得最小值
- 前缀和判定:按距离排序后,维护前缀加权和,当
tot >= sum - tot时,当前位置即为加权中位数
-
关键步骤:
- 读取输入:\(n\)(商店数量)、\(a[1..n]\)(商店坐标)、\(w[1..n]\)(商品重量)
- 计算总加权和:
sum = Σw[i] * a[i] - 排序:按距离从小到大排序
- 找加权中位数(遍历 \(i\) 从 \(1\) 到 \(n\)):
- 累加前缀和:
tot += a[i].w * a[i].d - 判定中位数:如果
tot >= sum - tot,则当前位置 \(a[i].d\) 为加权中位数
- 累加前缀和:
- 计算总成本:以加权中位数为选址位置,计算所有点到该位置的加权距离之和
- 输出结果:最小运费之和 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),主要是排序复杂度
- 空间复杂度:\(O(n)\),存储商店信息
-
加权中位数贪心的核心思想:
- 加权中位数最优性:在带权重的情况下,加权中位数是使得到所有点加权距离之和最小的位置
- 前缀和判定:通过维护前缀加权和,找到满足平衡条件的位置
- 贪心正确性:加权中位数使得左右两侧的加权和尽可能平衡,从而最小化总距离成本
- 与基础中位数的区别:基础中位数只考虑点的数量,加权中位数考虑点的权重
- 适用于带权选址问题、加权距离最小化、资源分配优化等问题
【算法标签】
贪心
【代码详解】
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, sum, ans; // sum: 总加权和, ans: 最小总成本
struct node
{
int w, d; // w: 权重, d: 距离
} a[N];
// 按距离从小到大排序
bool cmp(node x, node y)
{
return x.d < y.d;
}
signed main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i].d;
}
for (int i = 1; i <= n; i++)
{
cin >> a[i].w;
sum += a[i].w * a[i].d; // 计算总加权和
}
sort(a + 1, a + n + 1, cmp);
int tot = 0; // 前缀加权和
for (int i = 1; i <= n; i++)
{
tot += a[i].w * a[i].d;
// 当前缀加权和大于等于总加权和的一半时
if (tot >= sum - tot)
{
int pos = a[i].d; // 选择当前位置作为最优位置
for (int j = 1; j <= n; j++)
{
ans += abs(a[j].d - pos) * a[j].w; // 计算总成本
}
break;
}
}
cout << ans;
return 0;
}
【运行结果】
4
6 2 9 1
1 2 3 4
29
浙公网安备 33010602011771号