题解:洛谷 P11960 平均分配
【题目来源】
洛谷:P11960 [GESP202503 五级] 平均分配 - 洛谷(luogu.com.cn)
【题目描述】
小 A 有 \(2n\) 件物品,小 B 和小 C 想从小 A 手上买走这些物品。对于第 \(i\) 件物品,小 B 会以 \(b_i\) 的价格购买,而小 C 会以 \(c_i\) 的价格购买。为了平均分配这 \(2n\) 件物品,小 A 决定小 B 和小 C 各自只能买走恰好 \(n\) 件物品。你能帮小 A 求出他卖出这 \(2n\) 件物品所能获得的最大收入吗?
【输入】
第一行,一个正整数 \(n\)。
第二行,\(2n\) 个整数 \(b_1,b_2,…,b_{2n}\)。
第三行,\(2n\) 个整数 \(c_1,c_2,…,c_{2n}\)。
【输出】
一行,一个整数,表示答案。
【输入样例】
3
1 3 5 6 8 10
2 4 6 7 9 11
【输出样例】
36
【核心思想】
-
问题分析:给定 \(2n\) 件物品,每件物品有小 B 的购买价 \(b_i\) 和小 C 的购买价 \(c_i\)。要求小 B 和小 C 各买恰好 \(n\) 件,最大化总收入。这是一个排序 + 贪心分配问题,关键在于按 \(b_i - c_i\) 的差值排序,差值大的优先分配给 B,差值小的优先分配给 C。
-
算法选择:
- 差值排序:按 \(b_i - c_i\) 降序排列,将每件物品的" B 相对优势"量化
- 贪心分配:前 \(n\) 件给 B(差值大,B 出价远高于 C),后 \(n\) 件给 C(差值小甚至负,C 出价更高或接近)
-
关键步骤:
- 读入数据:\(n\),数组 \(b[1..2n]\) 和 \(c[1..2n]\)
- 计算差值:对每件物品 \(i\),计算
diff = b[i] - c[i] - 降序排序:按
diff从大到小排序 - 贪心累加:
- 前 \(n\) 件:
ans += b[i](分配给 B) - 后 \(n\) 件:
ans += c[i](分配给 C)
- 前 \(n\) 件:
- 输出
ans
-
时间/空间复杂度:
- 时间复杂度:\(O(n \log n)\),排序 \(O(n \log n)\),累加 \(O(n)\)
- 空间复杂度:\(O(n)\),存储结构体数组
-
差值排序贪心分配的核心思想:
- 边际收益分析:将物品 \(i\) 分配给 B 而非 C 的边际收益为 \(b_i - c_i\)。差值越大,分配给 B 的收益越高
- 排序创造单调性:按差值降序后,前 \(n\) 件是"最适合给 B"的物品,后 \(n\) 件是"最适合给 C"的物品
- 交换论证:若最优解中存在差值 \(d_i < d_j\) 但 \(i\) 给 B、\(j\) 给 C 的情况,交换两者分配可使总收入增加 \(d_j - d_i > 0\),矛盾。故排序后的分配为最优
- 约束满足:排序后前 \(n\) 件和后 \(n\) 件天然满足"各 \(n\) 件"的数量约束
- 适用于"两组各选 \(n\) 个、最大化总和"的分配问题,核心在于将分配决策转化为排序后的前缀/后缀选取
【算法标签】
普及 #贪心
【代码详解】
#include <bits/stdc++.h>
using namespace std;
#define int long long // 定义宏,将int替换为long long类型
const int N = 100005; // 定义数组的最大大小
int n; // 输入的n值
int b[2*N], c[2*N]; // 存储输入的b数组和c数组
// 定义结构体,存储b值、c值和它们的差值
struct node {
int b, c, diff; // b值,c值,b-c的差值
} a[2*N]; // 结构体数组
// 自定义排序函数,按差值从大到小排序
bool cmp(node x, node y) {
return x.diff > y.diff;
}
signed main() { // 使用signed代替int,因为宏定义了int为long long
cin >> n; // 输入n
// 输入b数组和c数组
for (int i = 1; i <= 2 * n; i++)
cin >> b[i];
for (int i = 1; i <= 2 * n; i++)
cin >> c[i];
// 初始化结构体数组,计算每个元素的差值
for (int i = 1; i <= 2 * n; i++)
a[i] = {b[i], c[i], b[i] - c[i]};
// 对结构体数组按差值从大到小排序
sort(a + 1, a + 2 * n + 1, cmp);
int ans = 0; // 初始化结果为0
// 前n个元素取b值
for (int i = 1; i <= n; i++)
ans += a[i].b;
// 后n个元素取c值
for (int i = n + 1; i <= 2 * n; i++)
ans += a[i].c;
// 输出最终结果
cout << ans << endl;
return 0;
}
【运行结果】
3
1 3 5 6 8 10
2 4 6 7 9 11
36
浙公网安备 33010602011771号