【GESP】C++四级真题 luogu-B4502, [GESP202603 四级] 礼盒排序
2026年3月,GESP四级真题,考察结构体封装与多关键字自定义排序逻辑,难度★★☆☆☆。洛谷难度级别:普及-。
B4502 [GESP202603 四级] 礼盒排序
题目要求
题目描述
商店推出了许多礼盒,每个礼盒中包含 $k$ 件商品,每件商品都有一个价格。 现在需要对这些礼盒进行排序,排序规则如下:
- 先按礼盒总价格从小到大排序;
- 如果总价格相同,按礼盒中最贵商品的价格从小到大排序;
- 如果仍然相同,按礼盒中最便宜商品的价格从小到大排序;
- 如果仍然相同,按礼盒编号从小到大排序。
请输出排序后的礼盒编号。
输入格式
第一行包含两个整数 $n$ 和 $k$,分别表示礼盒数量和每个礼盒中商品的数量。 接下来 $n$ 行,每行包含 $k$ 个整数,第 $i$ 行表示第 $i$ 个礼盒中各商品的价格。
输出格式
输出一行,包含排序后的礼盒编号(编号从 1 开始),用空格分隔。
输入输出样例 #1
输入 #1
1
2
3
4
5
4 3
3 5 2
4 1 5
2 2 4
3 4 3
输出 #1
1
3 4 2 1
说明/提示
样例解释
| 编号 | 商品价格 | 总价 | 最大值 | 最小值 |
|---|---|---|---|---|
| 1 | 3 5 2 | 10 | 5 | 2 |
| 2 | 4 1 5 | 10 | 5 | 1 |
| 3 | 2 2 4 | 8 | 4 | 2 |
| 4 | 3 4 3 | 10 | 4 | 3 |
排序过程:
- 按总价排序,3 号礼盒总价最小;
- 其余总价均为 10,再按最大值排序,4 号最大值更小;
- 1 号和 2 号最大值相同,再按最小值排序,2 号更小。
最终顺序为:3 4 2 1
数据范围
保证 $1 \le n \le 10^3$,$1 \le k \le 10$,$1 \le \text{商品价格} \le 10^4$。
题目分析
此题是标准的结构体多关键字排序(Struct Sorting)题目。
在普通的一维数组排序中,数字大小就是唯一的话语权。而在现实生活的复杂业务中(比如排成绩表、排商品),往往会有第一优先级、第二优先级甚至更多维度的对比考量。这就要求我们将这件“复杂物品”的所有特征打包进一个结构体(struct)中,然后为它专门编写一个用于对比大小的比较函数(cmp)。
在输入数据时,我们就要实时提取并计算出每个礼盒身上的“四大特征”,存入它专用的数据结构中:
- 自带属性(编号
id): 随着输入顺序,自然赋予 1 到 $n$。 - 汇总属性(总价
total): 将它包里的 $k$ 个商品价格累加。 - 极值属性(最大
max_val、最小min_val): 在读入 $k$ 个价格时,使用打擂台法实时算出最大和最小值。
信息收集完毕后,交给 std::sort 以及我们为其量身定制的 cmp 比较函数即可瞬间完成全套排列。
示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <vector>
#include <algorithm>
// 1. 定义礼盒结构体,把这件商品所有的考量维度打包在一起
struct Box {
int id; // 礼盒的编号
int total; // 礼盒商品总价
int max_val; // 最贵商品的价格
int min_val; // 最便宜商品的价格
};
// 2. 编写灵魂:自定义多条件比较函数
// 返回 true 代表希望 a 排在 b 的前面,返回 false 代表退让
bool cmp(const Box& a, const Box& b) {
// 规则 1:总价格从小到大
if (a.total != b.total) {
return a.total < b.total;
}
// 规则 2:总价若相同,最贵的大件越便宜越靠前
if (a.max_val != b.max_val) {
return a.max_val < b.max_val;
}
// 规则 3:若前两项都相同,看最便宜的小件,越便宜越靠前
if (a.min_val != b.min_val) {
return a.min_val < b.min_val;
}
// 规则 4:若以上全撞车了,编号小的优先
return a.id < b.id;
}
int main() {
int n, k;
std::cin >> n >> k;
// 创建有 n 个元素的礼盒大群组
std::vector<Box> boxes(n);
// 3. 读取并立刻计算加工数据
for (int i = 0; i < n; i++) {
boxes[i].id = i + 1; // 赋编号(注意从 1 开始编号)
int total = 0;
int max_v = -1; // 找最大值,初始化为极小
int min_v = 1000000; // 找最小值,初始化为极大
for (int j = 0; j < k; j++) {
int price;
std::cin >> price;
total += price;
if (price > max_v) {
max_v = price;
}
if (price < min_v) {
min_v = price;
}
}
// 把算出来的特征烙印在这个礼盒对应的结构体变量上
boxes[i].total = total;
boxes[i].max_val = max_v;
boxes[i].min_val = min_v;
}
// 4. 调用 C++ 排序大招,并挂载自己的判断规则
std::sort(boxes.begin(), boxes.end(), cmp);
// 5. 按排序后的顺序,挨个念它们的名字(编号)
for (int i = 0; i < n; i++) {
std::cout << boxes[i].id;
if (i < n - 1) {
std::cout << " ";
}
}
std::cout << std::endl;
return 0;
}
代码解析小贴士
cmp函数的最佳模板: 当涉及多重排版规则时,一层一层嵌套if-else会让代码飞离屏幕且极度丑陋。最优雅的写法是“层层阻击”:如果当前最重要指标两者不相等(a != b),立刻用当前指标决出胜负(return a < b;);如果相等,程序会自动漏到下一个阻击点。这样代码极其清爽且防伪。- 极大值与极小值的初始化: 在循环中使用打擂台法找最大值时,务必把擂主初始战斗力设为负无穷(比如
-1);找最小值时设为正无穷(比如10000000)。千万不要一拍脑袋全写成0,否则你的最小值永远都会是0(没人能比0低)。
所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
“luogu-”系列题目可在洛谷题库进行在线评测。
“bcqm-”系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
