P1042 [NOIP 2003 普及组] 乒乓球

📅 2026/7/18 16:43:40 👁️ 阅读次数 📝 编程学习
P1042 [NOIP 2003 普及组] 乒乓球

记录153

#include<bits/stdc++.h> using namespace std; // 定义一个函数来模拟特定分制的比赛过程 // s: 完整的比赛记录字符串, target: 目标分数(11或21) void solve(string s,int target){ int w=0,l=0; // 记录当前局华华和对手的得分 for(char c:s){ if(c=='W')w++; else if(c=='L')l++; // 判断当前局是否结束:一方达到目标分 且 分差>=2 if((w>=target||l>=target)&&abs(w-l)>=2){ cout<<w<<":"<<l<<"\n"; w=0; // 重置分数,开始新的一局 l=0; } } // 比赛记录结束后,如果当前局还有比分(哪怕没结束),也要输出 cout<<w<<":"<<l<<"\n"; } int main(){ ios::sync_with_stdio(false); cin.tie(0); string s,total=""; // 循环读取多行输入,直到遇到结束符 E while(cin>>s){ // 查找当前行是否有 'E' int pos=s.find('E'); if(pos!=string::npos){ // 如果找到了 E,只截取 E 之前的部分拼接到总记录中 total+=s.substr(0,pos); break; // 遇到 E 后忽略之后的所有内容,直接跳出循环 }else{ // 没有 E,直接把这一行拼接到总记录中 total+=s; } } // 先输出 11 分制的结果 solve(total,11); // 两部分之间由一个空行分隔 cout<<"\n"; // 再输出 21 分制的结果 solve(total,21); return 0; }

题目传送门https://www.luogu.com.cn/problem/P1042


前言

我是一名专注信奥赛(CSP-J/S、NOIP)的教练。

  • 如果你觉得这篇题解对你有帮助,欢迎点击关注我的CSDN账号,我会持续更新高质量算法解析。
  • 我深知算法思维的构建远比单纯通过题目更重要,本系列题解不局限于AC代码的堆砌,而是致力于拆解题目背后的逻辑链条与核心知识点
  • 备赛路上若遇瓶颈,欢迎随时评论或私信,我将甄选典型疑难问题,通过视频讲解或撰写专项文章的形式,为你提供深度答疑。

核心解题思路

这道题是一道非常经典的字符串处理与模拟问题。

  1. 问题转化与输入处理
    题目要求处理多行字符串,直到遇到字符 'E' 为止,并且忽略 'E' 之后的所有内容。由于输入可能跨越多行,我们需要将所有有效字符拼接成一个完整的字符串total

  2. 算法设计(统一模拟函数)
    题目要求分别输出 11 分制和 21 分制的比赛结果。两者的规则完全一致,只是获胜的分数线不同。因此,我们可以编写一个通用的solve函数,通过传入目标分数target来复用逻辑。
    在模拟过程中,我们需要维护当前局的比分wl。每当遇到 'W' 或 'L' 时更新比分,并检查是否满足结束条件:一方分数达到 target 且分差大于等于 2。如果满足,输出当前比分并重置分数;如果不满足,继续累加。最后,无论最后一局是否结束,都需要输出当前的比分。


代码分块详细解释

1. 核心逻辑:通用比赛模拟函数

// 定义一个函数来模拟特定分制的比赛过程 // s: 完整的比赛记录字符串, target: 目标分数(11或21) void solve(string s, int target){ int w=0, l=0; // 记录当前局华华和对手的得分 for(char c : s){ if(c == 'W') w++; else if(c == 'L') l++; // 判断当前局是否结束:一方达到目标分 且 分差>=2 if((w >= target || l >= target) && abs(w - l) >= 2){ cout << w << ":" << l << "\n"; w = 0; // 重置分数,开始新的一局 l = 0; } } // 比赛记录结束后,如果当前局还有比分(哪怕没结束),也要输出 cout << w << ":" << l << "\n"; }
  • 详细分析:这是本题的灵魂所在。
    • 比分更新:遍历字符串中的每个字符,如果是 'W' 则华华得分,如果是 'L' 则对手得分。
    • 结束条件判定if((w >= target || l >= target) && abs(w - l) >= 2)是乒乓球比赛规则的核心体现。必须同时满足两个条件:1. 有人先打到了目标分(11分或21分);2. 两人的分差至少为 2 分(防止出现 11:10 这种未拉开差距的情况)。
    • 重置与输出:一旦满足结束条件,立刻输出当前比分,并将wl清零,表示下一局比赛开始。
    • 处理未结束的最后一局:循环结束后,wl中可能还残留着最后一局未打完的比分(例如 1:1 或 2:1)。根据题目要求,必须将这局未完成的比分也输出。

2. 主函数:多行输入拼接与 E 字符处理

int main(){ ios::sync_with_stdio(false); cin.tie(0); string s, total = ""; // 循环读取多行输入,直到遇到结束符 E while(cin >> s){ // 查找当前行是否有 'E' int pos = s.find('E'); if(pos != string::npos){ // 如果找到了 E,只截取 E 之前的部分拼接到总记录中 total += s.substr(0, pos); break; // 遇到 E 后忽略之后的所有内容,直接跳出循环 } else { // 没有 E,直接把这一行拼接到总记录中 total += s; } }
  • 详细分析:这部分处理了题目中比较棘手的输入格式。
    • 多行拼接:使用while(cin >> s)循环读取每一行,并将它们拼接到total字符串中。
    • E 字符截断:使用s.find('E')检查当前行是否包含结束符。如果找到了,利用s.substr(0, pos)截取 'E' 之前的有效部分拼接到total,然后立刻break跳出循环,完美实现了“忽略 E 之后的所有内容”的要求。

3. 调用模拟函数与格式控制

// 先输出 11 分制的结果 solve(total, 11); // 两部分之间由一个空行分隔 cout << "\n"; // 再输出 21 分制的结果 solve(total, 21); return 0; }
  • 详细分析:主函数的最后部分非常简洁。先调用solve(total, 11)输出 11 分制下的所有局比分,然后输出一个换行符作为两部分结果的分隔,最后调用solve(total, 21)输出 21 分制的结果。

核心逻辑总结表

代码模块核心变量/操作精炼作用解决的痛点
多行拼接while(cin >> s)total += s将跨行的比赛记录合并为一个完整字符串解决了输入数据被换行符打断的问题,便于统一处理
E 字符截断s.find('E')substr定位结束符并截取有效数据严格遵守题目“忽略 E 之后所有内容”的特殊要求
比分更新if(c=='W') w++遍历字符串并实时累加双方得分将字符序列转化为具体的比赛分数
结束判定(w>=target || l>=target) && abs(w-l)>=2检查是否满足获胜条件完美还原了乒乓球“至少领先2分才能获胜”的复杂规则
局数重置w=0; l=0一局结束后清零当前比分确保下一局比赛从 0:0 开始,互不干扰
末局输出cout << w << ":" << l在循环结束后输出残留比分解决了最后一局比赛可能未打完但仍需输出比分的边界问题