P1042 [NOIP 2003 普及组] 乒乓球
1 题目背景
国际乒联现在主席沙拉拉自从上任以来就立志于推行一系列改革,以推动乒乓球运动在全球的普及。其中 11 分制改革引起了很大的争议,有一部分球员因为无法适应新规则只能选择退役。华华就是其中一位,他退役之后走上了乒乓球研究工作,意图弄明白 11 分制和 21 分制对选手的不同影响。在开展他的研究之前,他首先需要对他多年比赛的统计数据进行一些分析,所以需要你的帮忙。
2 题目描述
华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在 11 分制和 21 分制下,双方的比赛结果(截至记录末尾)。
比如现在有这么一份记录,(其中 W 表示华华获得一分,L 表示华华对手获得一分):
WWWWWWWWWWWWWWWWWWWWWWLW
在 11 分制下,此时比赛的结果是华华第一局 11 比 0 获胜,第二局 11 比 0 获胜,正在进行第三局,当前比分 1 比 1。而在 21 分制下,此时比赛结果是华华第一局 21 比 0 获胜,正在进行第二局,比分 2 比 1。如果一局比赛刚开始,则此时比分为 0 比 0。直到分差大于或者等于 2,才一局结束。
注意:当一局比赛结束后,下一局立刻开始。
你的程序就是要对于一系列比赛信息的输入(WL 形式),输出正确的结果。
3 输入格式
每个输入文件包含若干行字符串,字符串由大写的 W 、 L 和 E 组成。其中 E 表示比赛信息结束,程序应该忽略 E 之后的所有内容。
4 输出格式
输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。其中第一部分是 11 分制下的结果,第二部分是 21 分制下的结果,两部分之间由一个空行分隔。
5 输入输出样例
输入
WWWWWWWWWWWWWWWWWWWW
WWLWE
输出
11:0
11:0
1:1
21:0
2:1
6 说明/提示
每行至多 25 个字母,最多有 2500 行。
(注:事实上有一个测试点有 2501 行数据。)
【题目来源】
NOIP 2003 普及组第一题
7 最开始的思路
这是一道模拟题,其主要的思路是模拟两个人比分。我的思路是:
- 逐个字符读取获取到的字符串,如果遇到
\n
就跳过不读,读到E
就停止。 - 将
E
添加到读取的字符串末尾,用来表征结束。 - 接下来分别按照11分制和21分制来进行模拟,中间输出比分。如果读到
E
,或者两位同学分差大于等于2分且至少一位同学达到11/21分,就打印当前比分,并且把两位同学比分置0。如果读到的是W
,第一位同学加一分。如果读到的是L
,第二位同学加一分。 - 在两轮模拟中间加一行空行。
8 遇到的问题
- 在获取输入时,不仅是
\n
需要忽略掉,\r
和' '
也需要忽略掉。否则会读入多余的字符(这里我大意了,一开始只忽略了\n
,没有考虑其他的多余字符) - 可能读到
E
的时候刚好一局比完,这时候仍然要输出0:0
(这个点很坑)。因此读到E
和一局比完的情况需要分开考虑。 - 在进行
&&
和||
判断时需要加上括号,保证优先级。 - 在第二次模拟开始前,需要将两人的分数重新置0
- 最后要输出一行空行
9 正确实现
#include <iostream>
using namespace std;
int main(void)
{
string game;
char ch;
while((ch = getchar()) != 'E') {
if(ch != 13 && ch != ' ' && ch != 10)
game += ch;
}
game += 'E';
int Wp = 0;
int Lp = 0;
for(char ch: game) {
if((abs(Wp-Lp) >= 2 && (Wp >= 11 || Lp >= 11))) {
cout << Wp << ":" << Lp << endl;
Wp = 0;
Lp = 0;
}
if(ch == 'E') {
cout << Wp << ":" << Lp << endl;
break;
}
if(ch == 'W') {
Wp += 1;
}
else {
Lp += 1;
}
}
cout << endl;
Wp = 0;
Lp = 0;
for(char ch: game) {
if(((abs(Wp-Lp) >= 2) && (Wp >= 21 || Lp >= 21))) {
cout << Wp << ":" << Lp << endl;
Wp = 0;
Lp = 0;
}
if(ch == 'E') {
cout << Wp << ":" << Lp << endl;
break;
}
if(ch == 'W') {
Wp += 1;
}
else {
Lp += 1;
}
}
cout << endl;
return 0;
}
成功!
10 总结
该题提醒我们:
- 要谨慎考虑输入的方式和输入的字符串,在输入时要处理掉一切可能的多余字符;
- 要考虑到所有的
corner case
,谨慎读题; - 在多个判断时最好加上括号,保证优先级;
- 多次模拟中间变量要置0;
- 确保输出格式正确,换行和空行的数目正确;
11 深基参考题解
《深基》上给了一个版本的答案,但是我用其中一个测试点跑了,发现这个答案是通不过测试的。所以摆在这里仅供参考。
#include <iostream>
#include <cmath>
using namespace std;
int f[2] = {11,21}; // 将两轮分制
int a[25 * 2500 + 10], n = 0;
int main() {
char tmp;
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
while(1) {
cin >> tmp;
if(tmp == 'E') break;
else if(tmp == 'W') a[n++] = 1;
else if(tmp == 'L') a[n++] = 0;
}
for(int k = 0; k < 2; k++) {
int w = 0, l = 0;
for(int i = 0; i < n; i++) {
w += a[i];
l += l - a[i];
if((max(w,l) >= f[k]) && abs(w-l) >= 2) {
cout << w << ":" << l << endl;
w = l = 0;
}
}
cout << w << ":" << l << endl;
cout << endl;
}
return 0;
}