对于洛谷P1042乒乓球问题(模拟)的复盘与反思

题目描述

华华通过以下方式进行分析,首先将比赛每个球的胜负列成一张表,然后分别计算在 1111 分制和 2121 分制下,双方的比赛结果(截至记录末尾)。

比如现在有这么一份记录,(其中 WW 表示华华获得一分,LL 表示华华对手获得一分):

WWWWWWWWWWWWWWWWWWWWWWLWWWWWWWWWWWWWWWWWWWWWWWLW

在 1111 分制下,此时比赛的结果是华华第一局 1111 比 00 获胜,第二局 1111 比 00 获胜,正在进行第三局,当前比分 11 比 11。而在 2121 分制下,此时比赛结果是华华第一局 2121 比 00 获胜,正在进行第二局,比分 22 比 11。如果一局比赛刚开始,则此时比分为 00 比 00。直到分差大于或者等于 22,才一局结束。

你的程序就是要对于一系列比赛信息的输入(WLWL 形式),输出正确的结果。

输入格式

每个输入文件包含若干行字符串,字符串有大写的 WW 、 LL 和 EE 组成。其中 EE 表示比赛信息结束,程序应该忽略 EE 之后的所有内容。

输出格式

输出由两部分组成,每部分有若干行,每一行对应一局比赛的比分(按比赛信息输入顺序)。其中第一部分是 1111 分制下的结果,第二部分是 2121 分制下的结果,两部分之间由一个空行分隔。

输入输出样例

输入 #1复制

WWWWWWWWWWWWWWWWWWWW
WWLWE

输出 #1复制

11:0
11:0
1:1

21:0
2:1

说明/提示

每行至多 2525 个字母,最多有 25002500 行。

(注:事实上有一个测试点有 25012501 行数据。)

【题目来源】

NOIP 2003 普及组第一题

参考代码如下:

#include <iostream>
#include <cmath>
using namespace std;
int f[2] = {11, 21};
int a[25 * 2500 + 10], n = 0;
int main() {
    char tmp;
    while (1) {
        cin >> tmp;                             //用单个字符变量读入胜负情况
        if (tmp == 'E') break;                  //读到‘E’时直接结束读入
        else if (tmp == 'W') a[n++] = 1;        //华华赢
        else if (tmp == 'L') a[n++] = 0;        //华华输
    }
    for (int k = 0; k < 2; k++) {               //用for循环代替两种赛制
        int w = 0, l = 0;                       //w代表赢,l代表输
        for (int i = 0; i < n; i++) {           //遍历用于记录输赢情况的数组
            w += a[i]; l += 1 - 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;
}

尽管本题是难度较低的模拟算法入门题,但其对我的启发还是不小。

首先是题目中所要求的输入格式:每行至多 25 个字母。

在模拟类型的题目中,题目的输出格式往往比较令我头疼,在选择使用cin函数和scanf函数的时候有些不知道如何下手,尤其是对于字符串的读入比较令我头疼。自己遇到的问题包括但不限于以下几种。

1.读入字符串时忽略了空格也需要被读入,但使用了scanf函数。

        scanf函数工作的原理是不断地读入键盘上输入的字符串,直到遇见空格或回车就停下,并把空格和回车存储在缓冲区中等待下次读入。因此,空格并不会被scanf函数接受并赋值给string变量或者存放字符数组中。而解决读入空格问题需要用到fgets函数。

2.当题目中需要读入多行字符串时,往往会遇到行末的回车被读入到下一行字符串中,从而导致程序运行结果出错。

        如第1个问题中所描述的,scanf遇到 回车(enter),空格,TAB 就会结束一次输入,并且, scanf在一次输入结束后,不会舍弃最后的回车符(即回车符会残留在数据缓冲区中)。

先看以下代码:

#include <stdio.h>
int main(){
    char c1,c2;
    scanf("%c %c",&c1,&c2);   //这里有一个空格
    printf("%d %d\n",c1,c2);
 
    scanf("%c%c",&c1,&c2);  //这里没有空格
    printf("%d %d\n",c1,c2);
    return 0;
}

输入输出如下:

发现第二次输出scanf函数将回车\n读入到了c1变量。


那么该如何解决这种问题呢?我们可以这样改一下:

#include <stdio.h>
int main(){
    char c1,c2;
    scanf("%c %c",&c1,&c2);   //这里有一个空格
    printf("%d %d\n",c1,c2);
 
    scanf(" %c %c",&c1,&c2);   //这里也有了空格
    printf("%d %d\n",c1,c2);
    return 0;
}

在第二段代码中,第二个scanf函数读入的最前端加了一个空格(换成\n或者\t也可以),这样就把缓冲区中的回车当成第一个字符,读取后丢掉 。并且这样可以避免使用getchar()函数吸收掉回车而造成的问题。

可以很好理解scanf中 空格的作用:

空格( )即为读取一个结束字符然后丢掉,而普通的字符不受影响。

用好之后可以避免很多程序BUG。


因此,此题参考代码中的读入方法可以满足题目中要求每行最多25个字符,同时可以忽略掉回车对于读入这么长的字符串的影响。

该方法是不用字符数组或者string变量来存储读入的字符串,而是用单个的字符变量,通过while循环来边读边判断双方的胜负情况,这样可以完全忽略掉scanf读入回车的问题(当然这种问题的解决方法同上方法2.)

解决掉该题的读入问题后,后面的过程就按照题目要求逐步模拟即可轻松解决。

同时也要有意识地去使用cmath库中的max、abs等函数来作为判断条件。


本文仅作为自己学习轨迹的记录

参考:【C语言】scanf语句吃掉回车或者空格问题详解        作者:Z小旋

以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YUKIPEDIA~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值