文章

【GESP】C++四级真题 luogu-B4502, [GESP202603 四级] 礼盒排序

2026年3月,GESP四级真题,考察结构体封装与多关键字自定义排序逻辑,难度★★☆☆☆。洛谷难度级别:普及-

B4502 [GESP202603 四级] 礼盒排序

题目要求

题目描述

商店推出了许多礼盒,每个礼盒中包含 $k$ 件商品,每件商品都有一个价格。 现在需要对这些礼盒进行排序,排序规则如下:

  1. 先按礼盒总价格从小到大排序;
  2. 如果总价格相同,按礼盒中最贵商品的价格从小到大排序;
  3. 如果仍然相同,按礼盒中最便宜商品的价格从小到大排序;
  4. 如果仍然相同,按礼盒编号从小到大排序。

请输出排序后的礼盒编号。

输入格式

第一行包含两个整数 $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

说明/提示

样例解释

编号商品价格总价最大值最小值
13 5 21052
24 1 51051
32 2 4842
43 4 31043

排序过程:

  1. 按总价排序,3 号礼盒总价最小;
  2. 其余总价均为 10,再按最大值排序,4 号最大值更小;
  3. 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

在输入数据时,我们就要实时提取并计算出每个礼盒身上的“四大特征”,存入它专用的数据结构中:

  1. 自带属性(编号 id): 随着输入顺序,自然赋予 1 到 $n$。
  2. 汇总属性(总价 total): 将它包里的 $k$ 个商品价格累加。
  3. 极值属性(最大 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;
}

代码解析小贴士

  1. cmp 函数的最佳模板: 当涉及多重排版规则时,一层一层嵌套 if-else 会让代码飞离屏幕且极度丑陋。最优雅的写法是“层层阻击”:如果当前最重要指标两者不相等a != b),立刻用当前指标决出胜负(return a < b;);如果相等,程序会自动漏到下一个阻击点。这样代码极其清爽且防伪。
  2. 极大值与极小值的初始化: 在循环中使用打擂台法找最大值时,务必把擂主初始战斗力设为负无穷(比如 -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),考试认证学员交流,互帮互助

GESP/CSP 认证学习微信公众号
GESP/CSP 认证学习微信公众号
本文由作者按照 CC BY-NC-SA 4.0 进行授权