题解:洛谷 B4502 礼盒排序
【题目来源】
洛谷:B4502 [GESP202603 四级] 礼盒排序 - 洛谷
【题目描述】
商店推出了许多礼盒,每个礼盒中包含 \(k\) 件商品,每件商品都有一个价格。
现在需要对这些礼盒进行排序,排序规则如下:
- 先按礼盒总价格从小到大排序;
- 如果总价格相同,按礼盒中最贵商品的价格从小到大排序;
- 如果仍然相同,按礼盒中最便宜商品的价格从小到大排序;
- 如果仍然相同,按礼盒编号从小到大排序。
请输出排序后的礼盒编号。
【输入】
第一行包含两个整数 \(n\) 和 \(k\),分别表示礼盒数量和每个礼盒中商品的数量。
接下来 \(n\) 行,每行包含 \(k\) 个整数,第 \(i\) 行表示第 \(i\) 个礼盒中各商品的价格。
【输出】
输出一行,包含排序后的礼盒编号(编号从 \(1\) 开始),用空格分隔。
【输入样例】
4 3
3 5 2
4 1 5
2 2 4
3 4 3
【输出样例】
3 4 2 1
【核心思想】
-
问题分析:给定 \(n\) 个礼盒,每个礼盒包含 \(k\) 件商品的价格。需要按四重优先级排序:礼盒总价格升序 → 最贵商品价格升序 → 最便宜商品价格升序 → 礼盒编号升序。这是一个多关键字自定义排序问题,核心在于正确提取每个礼盒的统计特征并按优先级比较。
-
算法选择:
- 结构体封装:将礼盒的编号、总价格、最大价格、最小价格封装为结构体
- 自定义比较器:按四重优先级定义比较函数,配合排序算法完成排序
-
关键步骤:
- 读入数据:读取 \(n, k\)
- 预处理每个礼盒(\(i\) 从 \(1\) 到 \(n\)):
- 读入 \(k\) 个价格,同时计算:
- \(tot \leftarrow\) 所有价格之和
- \(maxn \leftarrow\) 最大价格
- \(minn \leftarrow\) 最小价格
- 存储结构体:\(a[i] = \{i, maxn, minn, tot\}\)
- 读入 \(k\) 个价格,同时计算:
- 自定义排序(比较函数 \(cmp\)):
- 若 \(x.tot \neq y.tot\):返回 \(x.tot < y.tot\)
- 否则若 \(x.maxn \neq y.maxn\):返回 \(x.maxn < y.maxn\)
- 否则若 \(x.minn \neq y.minn\):返回 \(x.minn < y.minn\)
- 否则:返回 \(x.id < y.id\)
- 输出结果:按排序后的顺序输出 \(a[i].id\)
-
时间/空间复杂度:
- 时间复杂度:\(O(n \cdot k + n \log n)\),预处理每个礼盒为 \(O(k)\),排序为 \(O(n \log n)\)
- 空间复杂度:\(O(n)\),存储 \(n\) 个结构体
-
多关键字排序的核心思想:
- 统计特征提取:在读入时同步计算总价格、最大价格、最小价格,避免二次遍历,将 \(O(n \cdot k)\) 的预处理与读入合并
- 优先级链式比较:比较函数按优先级从高到低依次判断,高优先级不同时直接返回结果,高优先级相同时才比较下一级,确保排序结果符合题意
- 稳定排序非必需:由于第四优先级是唯一的礼盒编号,保证不会出现完全相同的比较结果,因此无需稳定排序
- 结构体绑定策略:将编号与计算得到的统计量绑定在一起,排序后仍能追溯原始身份,避免排序后丢失对应关系
- 适用于多维度排序、需要按复合条件排列的自定义排序类问题
【算法标签】
普及- #其他排序
【代码详解】
#include <bits/stdc++.h> // 包含所有标准库头文件
using namespace std; // 使用标准命名空间
const int N = 1005; // 定义常量N,表示最大学生数量
int n, k; // n: 学生数量, k: 每个学生的成绩数量
// 定义学生结构体
struct Node
{
int id; // 学生编号
int maxn; // 该学生的最高成绩
int minn; // 该学生的最低成绩
int tot; // 该学生的总成绩
} a[N]; // 声明结构体数组a,用于存储所有学生的信息
// 自定义比较函数,用于排序
bool cmp(Node x, Node y)
{
// 第一优先级:按总成绩从小到大排序
if (x.tot != y.tot)
{
return x.tot < y.tot;
}
// 第二优先级:总成绩相同,按最高成绩从小到大排序
else if (x.maxn != y.maxn)
{
return x.maxn < y.maxn;
}
// 第三优先级:总成绩和最高成绩都相同,按最低成绩从小到大排序
else if (x.minn != y.minn)
{
return x.minn < y.minn;
}
// 第四优先级:所有成绩都相同,按学号从小到大排序
return x.id < y.id;
}
int main() // 主函数入口
{
cin >> n >> k; // 输入学生数量n和每个学生的成绩数量k
// 读取每个学生的k个成绩
for (int i = 1; i <= n; i++)
{
int x; // 临时变量,用于读取每个成绩
cin >> x; // 读取第一个成绩
int mx, mn, sum = 0; // mx: 最高成绩, mn: 最低成绩, sum: 总成绩
mx = mn = x; // 初始化最高成绩和最低成绩为第一个成绩
sum += x; // 将第一个成绩加入总成绩
// 读取剩余的k-1个成绩
for (int j = 2; j <= k; j++)
{
cin >> x; // 读取下一个成绩
sum += x; // 将成绩加入总成绩
// 更新最高成绩
if (x > mx)
{
mx = x;
}
// 更新最低成绩
if (x < mn)
{
mn = x;
}
}
// 将计算得到的值存入结构体数组
a[i].id = i; // 学号(从1开始的序号)
a[i].maxn = mx; // 最高成绩
a[i].minn = mn; // 最低成绩
a[i].tot = sum; // 总成绩
}
// 使用自定义比较函数对数组进行排序
// 注意:数组下标从1开始,所以排序范围是a+1到a+n+1
sort(a + 1, a + n + 1, cmp);
// 输出排序后的学号
for (int i = 1; i <= n; i++)
{
cout << a[i].id << " ";
}
cout << endl; // 输出换行
return 0; // 程序正常结束
}
【运行结果】
4 3
3 5 2
4 1 5
2 2 4
3 4 3
3 4 2 1
浙公网安备 33010602011771号