PTA L2-009 抢红包题解:用C++结构体+运算符重载搞定复杂排序(附完整代码)
·
PTA L2-009 抢红包题解:C++结构体与运算符重载的实战艺术
当面对需要处理多重排序规则的算法题时,很多初学者会陷入复杂的条件判断陷阱。这道PTA抢红包题目恰好提供了一个绝佳案例,让我们看到C++结构体封装与运算符重载如何将复杂问题优雅简化。不同于简单调用排序函数,这里需要处理金额、红包数量、编号三重排序维度,这正是运算符重载大显身手的舞台。
1. 问题本质与数据结构设计
这道题的核心在于正确处理三类数据关联:
- 人员标识 :从1到N的连续编号
- 财务流水 :既要记录收入也要扣除发出金额
- 红包计数 :每人抢到的红包总数
传统思路可能会选择三个独立数组分别存储这些信息,但结构体的优势在于将逻辑相关的数据捆绑管理:
struct People {
int id; // 人员编号
int cnt; // 抢到红包次数
double mon; // 净收入(单位:分)
};
这种封装方式使得代码更符合现实世界的业务逻辑——我们操作的是"人"这个完整实体,而非分散的数据片段。当处理每笔交易时,可以直观地更新对应结构体的成员变量。
2. 运算符重载的精妙实现
题目要求的排序规则包含三个层次:
- 主要按净收入降序
- 收入相同时按抢红包次数降序
- 前两者都相同时按编号升序
运算符重载将这些规则封装在结构体内部,使排序逻辑自成一体:
bool operator< (const People t) const {
if(abs(mon - t.mon) > 1e-4)
return mon > t.mon;
else if(cnt != t.cnt)
return cnt > t.cnt;
else
return id < t.id;
}
几个关键技术细节:
- 浮点数精度处理 :使用
1e-4作为比较阈值,避免直接==可能带来的误差 - 多级条件判断 :通过if-else链实现规则的优先级顺序
- 反向排序技巧 :降序排列通过
>运算符实现,升序则用<
3. 交易处理的完整流程
主程序的逻辑清晰地分为三个阶段:
3.1 数据初始化
int n;
scanf("%d",&n);
for(int i=0; i<n; i++) {
a[i+1].id = i+1; // 初始化编号
}
3.2 实时交易处理
int k;
scanf("%d",&k);
while(k--) {
int id;
double mon;
scanf("%d%lf",&id,&mon);
a[id].mon += mon; // 收红包方增加金额
a[id].cnt++; // 收红包方计数增加
a[i+1].mon -= mon; // 发红包方扣除金额
}
3.3 排序与输出
sort(a+1, a+1+n); // 使用重载的运算符排序
for(int i=1; i<=n; i++) {
printf("%d %.2lf\n", a[i].id, a[i].mon/100);
}
4. 关键问题与替代方案对比
4.1 浮点数精度陷阱
直接比较浮点数相等是危险的,本题采用两种策略:
- 金额比较时使用阈值
1e-4 - 以分为单位存储,最后输出时转换
提示:金融类计算建议始终使用整数最小单位(如分、厘),仅在最终展示时转换
4.2 实现方案对比
| 方法 | 优点 | 缺点 |
|---|---|---|
| 结构体+运算符重载 | 逻辑内聚,代码简洁 | 需要理解运算符重载概念 |
| vector+自定义比较函数 | 不修改数据结构 | 比较逻辑分散 |
| 多数组分别维护 | 实现简单 | 关联性差,易出错 |
5. 工程实践中的扩展思考
实际开发中,这类问题可能还需要考虑:
- 并发处理 :多线程环境下如何保证交易原子性
- 数据持久化 :交易记录的存储与恢复
- 异常处理 :无效输入、数据越界等情况
虽然本题简化了这些复杂因素,但良好的代码结构为后续扩展奠定了基础。例如,可以轻松添加成员函数来处理更复杂的业务逻辑:
void receivePacket(double amount) {
mon += amount;
cnt++;
// 可在此添加日志记录等扩展功能
}
6. 调试技巧与常见错误
在实现过程中,有几个典型错误值得警惕:
-
数组越界 :人员编号从1开始,但数组从0开始
- 解法:声明为
a[N+1]并始终使用1-based索引
- 解法:声明为
-
单位混淆 :题目输入是分,但输出要求元
- 解法:最后输出时除以100
-
排序范围错误 :
sort(a+1, a+1+n)而非sort(a, a+n) -
初始化遗漏 :未初始化cnt导致计数错误
- 最佳实践:为结构体添加构造函数
People() : id(0), cnt(0), mon(0.0) {}
7. 性能优化与复杂度分析
虽然题目数据规模(N≤1e4)对现代计算机压力不大,但了解算法复杂度仍有价值:
- 时间复杂度 :O(NK + NlogN)
- NK来自输入处理
- NlogN来自排序操作
- 空间复杂度 :O(N)
可能的优化方向:
- 使用更快的输入方法(如
ios::sync_with_stdio(false)) - 当K很小时(本题K≤20),NK项远小于NlogN,优化重点应在排序
实际测试中,这段代码在PTA平台运行时间通常在50ms以内,完全满足要求。
更多推荐

所有评论(0)