跳转至

BISHI19 乒乓球

简单通过率 34.53%python3样例通过牛客 AC

牛客原题  源码

讲解章节条件与循环模拟

一句话

同一份 W/L 记录,分别按 11 分制和 21 分制切局并输出比分。

解题思路

这题考什么

线性扫描 + 状态复位的模拟。记录本身不需要预处理:从头到尾读球, 每读一球给对应一方加 1 分,然后立刻检查「本局是否已经结束」, 结束就把这一局的比分记下来并把双方分数清零,进入下一局。

因为 11 分制和 21 分制的判定逻辑一模一样、只差一个目标分数, 所以把整个过程抽成函数 split_games(record, target),跑两遍就行, 不必把两套逻辑抄两遍——抄两遍最容易改了一处忘了另一处。

数据规模与复杂度

|s| <= 1e5,两种赛制各扫一遍,O(|s|)。
输出的行数最多也只有 |s| / 2 量级,攒进列表最后一次 write 即可。

规则:某方分数 >= 目标分(11 或 21)双方分差 >= 2 时本局结束,比分清零开新局。

坑在哪

  1. 结束条件是「>= 目标分 且 领先 2 分」,不是「等于目标分」。 13:11、15:13 都是合法的终局比分,写成 a == target or b == target 会在 10:10 之后一路判错;
  2. 记录读完后当前局若未结束也要输出比分,包括上一球刚好把一局打完、 新局比分还是 0:0 的情况——此时仍然要输出一行 "0:0"。 这是本题(以及它的原型 NOIP 题)最常见的失分点: 循环结束后无条件补一行,不要加「比分不为 0 才输出」的判断;
  3. 11 分制的结果和 21 分制的结果之间要空一行分隔, 实现上就是在两段结果中间插入一个空字符串再 join;
  4. 两次调用必须都从原始记录 s 出发。函数内部用的是局部变量 a、b, 不会互相污染,这也是把它写成函数而非内联两段的好处之一。

样例复核

记录是 22 个 W、一个 L、一个 W。 11 分制:前 11 个 W 打成 11:0,再 11 个 W 又是 11:0,剩下 W、L 得 1:1; 21 分制:前 21 个 W 打成 21:0,剩下 W、L、W 得 2:1。与样例一致。

参考实现

solutions/BISHI19.py
import sys


def split_games(record, target):
    """按目标分 target(11 或 21)把记录切成若干局,返回每局比分的字符串列表。"""
    res, a, b = [], 0, 0                   # a 是旺仔哥哥的分,b 是对手的分
    for ch in record:
        if ch == "W":
            a += 1
        else:
            b += 1
        # 有一方到达目标分且领先至少 2 分,本局结束,记录比分并清零开新局
        if (a >= target or b >= target) and abs(a - b) >= 2:
            res.append("%d:%d" % (a, b))
            a = b = 0
    res.append("%d:%d" % (a, b))   # 未打完的当前局(可能是 0:0)也要输出
    return res


s = sys.stdin.buffer.read().decode().strip()      # strip 掉行尾换行,只留 W/L
# 中间那个空字符串在 join 之后就是两部分之间的空行
out = split_games(s, 11) + [""] + split_games(s, 21)
sys.stdout.write("\n".join(out) + "\n")
[:octicons-arrow-left-16: BISHI18](BISHI18.md) [BISHI20 :octicons-arrow-right-16:](BISHI20.md)