P1042 [NOIP 2003 普及组] 乒乓球
记录153#includebits/stdc.h using namespace std; // 定义一个函数来模拟特定分制的比赛过程 // s: 完整的比赛记录字符串, target: 目标分数(11或21) void solve(string s,int target){ int w0,l0; // 记录当前局华华和对手的得分 for(char c:s){ if(cW)w; else if(cL)l; // 判断当前局是否结束一方达到目标分 且 分差2 if((wtarget||ltarget)abs(w-l)2){ coutw:l\n; w0; // 重置分数开始新的一局 l0; } } // 比赛记录结束后如果当前局还有比分哪怕没结束也要输出 coutw:l\n; } int main(){ ios::sync_with_stdio(false); cin.tie(0); string s,total; // 循环读取多行输入直到遇到结束符 E while(cins){ // 查找当前行是否有 E int poss.find(E); if(pos!string::npos){ // 如果找到了 E只截取 E 之前的部分拼接到总记录中 totals.substr(0,pos); break; // 遇到 E 后忽略之后的所有内容直接跳出循环 }else{ // 没有 E直接把这一行拼接到总记录中 totals; } } // 先输出 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代码的堆砌而是致力于拆解题目背后的逻辑链条与核心知识点备赛路上若遇瓶颈欢迎随时评论或私信我将甄选典型疑难问题通过视频讲解或撰写专项文章的形式为你提供深度答疑。核心解题思路这道题是一道非常经典的字符串处理与模拟问题。问题转化与输入处理题目要求处理多行字符串直到遇到字符 E 为止并且忽略 E 之后的所有内容。由于输入可能跨越多行我们需要将所有有效字符拼接成一个完整的字符串total。算法设计统一模拟函数题目要求分别输出 11 分制和 21 分制的比赛结果。两者的规则完全一致只是获胜的分数线不同。因此我们可以编写一个通用的solve函数通过传入目标分数target来复用逻辑。在模拟过程中我们需要维护当前局的比分w和l。每当遇到 W 或 L 时更新比分并检查是否满足结束条件一方分数达到 target 且分差大于等于 2。如果满足输出当前比分并重置分数如果不满足继续累加。最后无论最后一局是否结束都需要输出当前的比分。代码分块详细解释1. 核心逻辑通用比赛模拟函数// 定义一个函数来模拟特定分制的比赛过程 // s: 完整的比赛记录字符串, target: 目标分数(11或21) void solve(string s, int target){ int w0, l0; // 记录当前局华华和对手的得分 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 这种未拉开差距的情况。重置与输出一旦满足结束条件立刻输出当前比分并将w和l清零表示下一局比赛开始。处理未结束的最后一局循环结束后w和l中可能还残留着最后一局未打完的比分例如 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(cW) w遍历字符串并实时累加双方得分将字符序列转化为具体的比赛分数结束判定(wtarget || ltarget) abs(w-l)2检查是否满足获胜条件完美还原了乒乓球“至少领先2分才能获胜”的复杂规则局数重置w0; l0一局结束后清零当前比分确保下一局比赛从 0:0 开始互不干扰末局输出cout w : l在循环结束后输出残留比分解决了最后一局比赛可能未打完但仍需输出比分的边界问题