Leetcode 1029. 两地调度¶
两地调度
公司计划面试 2n 人。给你一个数组 costs,其中 costs[i] = [aCosti, bCosti] 。第 i 人飞往 a 市的费用为 aCosti ,飞往 b 市的费用为 bCosti 。
返回将每个人都飞到 a 、b 中某座城市的最低费用,要求每个城市都有 n 人抵达。
提示
2 * n == costs.length2 <= costs.length <= 100costs.length为偶数- 1 <= aCosti, bCosti <= 1000
示例1
输入:costs = [[10,20],[30,200],[400,50],[30,20]]
输出:110
解释:
第一个人去 a 市,费用为 10。 第二个人去 a 市,费用为 30。 第三个人去 b 市,费用为 50。 第四个人去 b 市,费用为 20。
最低总费用为 10 + 30 + 50 + 20 = 110,每个城市都有一半的人在面试。
示例2
输入:costs = [[259,770],[448,54],[926,667],[184,139],[840,118],[577,469]]
输出:1859
示例3
输入:costs = [[515,563],[451,713],[537,709],[343,819],[855,779],[457,60],[650,359],[631,42]]
输出:3086
思考¶
暴力解¶
一个简单的想法是,对每个人讨论去 a 地和去 b 地两种情况。同 Leetcode 514. 一样,很容易写出一个递归函数 f(consts, i, a, b),其中 consts 是不变的参数,i, a, b 用于表示当前的状态,即:前 i-1 个人已经分配完毕,开始分配第 i 个人,此时,a 地已经分配 a 人,b 地已经分配 b 人。f 返回这一状态下的最小代价。当然,数据量不允许这样暴力求解。
int f(std::vector<std::array<int, 2>> &costs, int i, int a, int b) {
if (i == costs.size()) {
return 0;
}
int ans1 = INF, ans2 = INF;
if (a > 0) {
ans1 = f(costs, i + 1, a - 1, b) + costs[i][0];
}
if (b > 0) {
ans2 = f(costs, i + 1, a, b - 1) + costs[i][1];
}
return std::min(ans1, ans2);
}
贪心¶
不难注意到:记 dif 为某个人去 a 地的花费和去 b 地的花费的差(不取绝对值),如果这个差很小,说明他去 a 地更好,反之去 b 地更好。因此我们考虑一个贪心策略,对 costs 排序(按 dif 升序),然后让前一半去 a 地,后一半去 b 地。
struct Cmp {
bool operator()(const std::array<int, 2>& a, const std::array<int, 2>& b) const {
int dif1 = a[0] - a[1], dif2 = b[0] - b[1];
return dif1 < dif2;
}
};
int solve2(std::vector<std::array<int, 2>>& costs) {
int n = costs.size() >> 1;
std::sort(costs.begin(), costs.end(), Cmp());
int ans = 0;
for (int i = 0; i < n; i++) {
ans += costs[i][0];
}
for (int i = n; i < n << 1; i++) {
ans += costs[i][1];
}
return ans;
}
对数器¶
证明贪心是困难的,因此在手上有一个暴力解的时候,写一个对数器做实验证明是(对菜鸡而言)更合理的选择。
int n = 14;
vector<array<int, 2>> costs(n);
for (int i = 0; i < n; i++) {
int a = rand() % 1000 + 1;
int b = rand() % 1000 + 1;
costs[i] = {a, b};
}
int solvel(vector<array<int, 2>>& costs);
int solve2(vector<array<int, 2>>& costs);
int ans1 = solvel(costs), ans2 = solve2(costs);
if (ans1 != ans2) {
cout << "[";
for (int i = 0; i < n; i++) {
cout << "[" << costs[i][0] << ", " << costs[i][1] << "]";
if (i < n - 1) cout << ", ";
}
cout << "]" << endl;
cout << ans1 << " " << ans2 << endl;
}
实验证明上述贪心策略在经验上是正确的。那么,提交。
代码¶
结果¶
结果的确如此。
