1. 题目背景与需求分析
东华OJ基础题第91题"乒乓球"是一道典型的算法模拟题,主要考察选手对流程控制和边界条件的处理能力。题目模拟乒乓球比赛的计分规则,要求根据输入的比赛记录字符串,分别输出11分制和21分制下的比赛结果。
这类题目在编程初学者中具有典型意义:
- 规则明确但细节繁琐(比分交替、领先规则、局点判断)
- 需要处理字符串解析和状态转换
- 包含多条件判断和循环控制
- 输出格式要求严格
2. 核心算法设计
2.1 输入输出规范
输入为一行由'E'结尾的字符串,包含W(华华得分)和L(对手得分)两种字符。需要分别处理11分制和21分制两种赛制,每组结果间用空行分隔。
示例输入:
code复制WWWWWWWWWWWEEEEEEEEEE
预期输出:
code复制11:0
0:0
2.2 比赛规则解析
乒乓球比赛的特殊计分规则:
- 先达到11分(21分)且领先至少2分者胜一局
- 若10:10(20:20)则需连续领先2分
- 一局结束后立即开始下一局
- 比赛可能在任意一局结束后终止
2.3 算法流程图
- 初始化比分计数器(w11, l11, w21, l21)
- 遍历输入字符串:
- 遇到'W'则对应赛制华华得分+1
- 遇到'L'则对手得分+1
- 检查是否达到局点条件
- 局点判断:
- 11分制:任一选手≥11分且领先≥2分
- 21分制:任一选手≥21分且领先≥2分
- 达到局点后:
- 输出当前比分
- 重置计数器
- 遇到'E'结束处理,输出未完成局的比分
3. C++实现详解
3.1 数据结构选择
cpp复制struct Score {
int w = 0;
int l = 0;
};
使用结构体存储比分更符合业务语义,比单独使用两个int变量更清晰。
3.2 核心处理逻辑
cpp复制void processMatch(const string& records) {
Score s11, s21;
vector<pair<int, int>> result11, result21;
for (char c : records) {
if (c == 'E') break;
// 更新比分
if (c == 'W') { s11.w++; s21.w++; }
else if (c == 'L') { s11.l++; s21.l++; }
// 检查11分制局点
if ((s11.w >= 11 || s11.l >= 11) && abs(s11.w - s11.l) >= 2) {
result11.emplace_back(s11.w, s11.l);
s11 = Score(); // 重置比分
}
// 检查21分制局点(类似逻辑)
// ...
}
// 处理未完成的局
if (s11.w != 0 || s11.l != 0) {
result11.emplace_back(s11.w, s11.l);
}
// 21分制同理...
}
3.3 输出处理技巧
cpp复制void printResults(const vector<pair<int, int>>& results) {
for (const auto& [w, l] : results) {
cout << w << ":" << l << endl;
}
if (results.empty()) {
cout << "0:0" << endl;
}
}
注意处理空结果的情况,这是OJ题常见的边界条件。
4. 常见问题与调试技巧
4.1 易错点分析
-
局点判断条件不完整:
- 错误:只判断≥11分,忽略领先2分条件
- 错误:10:10时未处理延长比赛情况
-
输入处理不完整:
- 未处理'E'之后的字符
- 未考虑空输入情况
-
输出格式错误:
- 结果间缺少空行
- 未完成的局忘记输出
4.2 测试用例设计
有效测试用例应包含:
- 常规情况:
code复制WWWWWWWWWWWLLLLLLLLLLLE - 延长比赛:
code复制WLWLWLWLWLWLWLWLWLWLWLWLE - 边界情况:
code复制E - 混合情况:
code复制WWLWLWLWWLLWLLWWWWWLLWWE
4.3 调试建议
- 使用小规模输入手动验证
- 添加中间输出调试比分变化
- 特别注意10:10和20:20时的处理
- 使用assert检查不变量
5. 算法优化与扩展
5.1 性能优化
虽然题目数据量不大,但可以优化:
- 预分配vector空间避免多次扩容
- 使用string_view减少字符串拷贝
- 循环展开处理批量字符
5.2 扩展方向
- 支持更多赛制(如7分制)
- 添加比赛回放功能
- 可视化比分展示
- 支持实时输入处理
关键提示:在实际OJ提交时,务必删除调试输出,避免因多余输出导致判题失败。建议封装调试宏:
cpp复制#define DEBUG 0 #if DEBUG #define debug(x) cout << #x << "=" << x << endl #else #define debug(x) #endif
这道题虽然归类为基础题,但完整处理所有边界条件需要细致的设计。建议初学者先画出状态转换图,再着手编码,比直接写代码更容易发现逻辑漏洞。我在实际解题时,就曾因忽略10:10平局后的特殊规则而多次提交失败。后来通过添加专门的平局标志变量才最终通过。
