[NOIP 2014 提高组] 生活大爆炸版石头剪刀布

GitHub跳转原题关系图返回列表

用胜负表判断每轮结果,并用取模从双方周期序列中取当前手势。

OJ: luogu

题目 ID: P1328

难度:普及-

标签:模拟周期python

日期: 2026-07-15 21:35

题意

小 A 和小 B 各有一个周期出拳序列,一共比赛 N 轮。每轮按五种手势的胜负关系决定得分,平局不得分。输出两人的总分。

思路

先把胜负关系写成表 win[a][b]

text
win[a][b] = 1 表示手势 a 能赢手势 b

round_index 轮时:

python
gesture_a = pattern_a[round_index % length_a]
gesture_b = pattern_b[round_index % length_b]

用取模就能循环使用周期序列。然后查表给胜者加分。

这题是周期模拟和查表练习,不创建 brute.py

Python 知识

  • /home/rainboy/mycode/hugo-blog/content/program_language/python/input_output_and_strings.md:整数数组用 list(map(int, input().split())) 读取。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/collections_toolkit.md:二维列表可以作为固定规则表使用。
  • % length 是周期访问的常用写法。
  • 查表比写大量 if 更稳定。

代码

python
win = [
    [0, 0, 1, 1, 0],
    [1, 0, 0, 1, 0],
    [0, 1, 0, 0, 1],
    [0, 0, 1, 0, 1],
    [1, 1, 0, 0, 0],
]

n, length_a, length_b = map(int, input().split())
pattern_a = list(map(int, input().split()))
pattern_b = list(map(int, input().split()))

score_a = 0
score_b = 0

for round_index in range(n):
    gesture_a = pattern_a[round_index % length_a]
    gesture_b = pattern_b[round_index % length_b]
    if win[gesture_a][gesture_b]:
        score_a += 1
    elif win[gesture_b][gesture_a]:
        score_b += 1

print(score_a, score_b)
cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-07-27 00:00
 * update_at: 2026-07-27 00:00
 */
#include <bits/stdc++.h>
using namespace std;

// win[a][b] = 1 表示手势 a 能赢手势 b
int win[5][5] = {
    {0, 0, 1, 1, 0},
    {1, 0, 0, 1, 0},
    {0, 1, 0, 0, 1},
    {0, 0, 1, 0, 1},
    {1, 1, 0, 0, 0},
};

const int MAXLEN = 205;

int n, len_a, len_b;
int a[MAXLEN]; // A 的出拳周期
int b[MAXLEN]; // B 的出拳周期

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> len_a >> len_b;
    for (int i = 0; i < len_a; i++) cin >> a[i];
    for (int i = 0; i < len_b; i++) cin >> b[i];

    int score_a = 0, score_b = 0;

    // 模拟 n 轮
    for (int i = 0; i < n; i++) {
        int ga = a[i % len_a]; // 取模得到当前手势
        int gb = b[i % len_b];
        if (win[ga][gb])       // A 赢
            score_a++;
        else if (win[gb][ga])  // B 赢
            score_b++;
        // 平局不处理
    }

    cout << score_a << " " << score_b << "\n";

    return 0;
}
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200 + 10;
int n, na, nb, a[MAXN], b[MAXN], cnta, cntb;
// vs[x][y] = 1 表示甲出 x、乙出 y 时甲赢,0 表示甲输或平
int vs[5][5] = {{0,0,1,1,0},{1,0,0,1,0},{0,1,0,0,1},{0,0,1,0,1},{1,1,0,0,0}};
int main()
{
    cin >> n >> na >> nb;
    for(int i = 0; i < na; i++) cin >> a[i];
    for(int i = 0; i < nb; i++) cin >> b[i];
    for(int i = 0; i < n; i++)
    {
        // 周期循环:a[i%na] 取到第 i 轮 A 的出手
        cnta += vs[a[i % na]][b[i % nb]];
        cntb += vs[b[i % nb]][a[i % na]];
    }
    cout << cnta << " " << cntb << endl;
    return 0;
}

复杂度

模拟 N 轮,每轮常数时间,时间复杂度是 O(N)O(N),空间复杂度是 O(NA+NB)O(N_A+N_B)

总结

周期模拟题通常用下标取模;规则复杂但规模固定时,用表格表达胜负关系更清楚。