洛谷P1328 - 生活大爆炸版石头剪刀布 - 题解
题目背景
NOIP2014 提高组 D1T1
题目描述
石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头。如果两个人出拳一样,则不分胜负。在《生活大爆炸》第二季第 8 集中出现了一种石头剪刀布的升级版游戏。
升级版游戏在传统的石头剪刀布游戏的基础上,增加了两个新手势:
斯波克:《星际迷航》主角之一。
蜥蜴人:《星际迷航》中的反面角色。
这五种手势的胜负关系如表一所示,表中列出的是甲对乙的游戏结果。

现在,小 A 和小 B 尝试玩这种升级版的猜拳游戏。已知他们的出拳都是有周期性规律的,但周期长度不一定相等。例如:如果小 A 以 石头-布-石头-剪刀-蜥蜴人-斯波克 长度为 石头-布-石头-剪刀-蜥蜴人-斯波克-石头-布-石头-剪刀-蜥蜴人-斯波克-...,而如果小 B 以 剪刀-石头-布-斯波克-蜥蜴人 长度为 剪刀-石头-布-斯波克-蜥蜴人-剪刀-石头-布-斯波克-蜥蜴人-...。
已知小 A 和小 B 一共进行
输入格式
第一行包含三个整数:
第二行包含 剪刀,石头,布,蜥蜴人,斯波克。数与数之间以一个空格分隔。
输出格式
输出一行,包含两个整数,以一个空格分隔,分别表示小 A、小 B 的得分。
输入输出样例 #1
输入 #1
1 | 10 5 6 |
输出 #1
1 | 6 2 |
输入输出样例 #2
输入 #2
1 | 9 5 5 |
输出 #2
1 | 4 4 |
说明/提示
对于
解题思路
破解本题的关键是把提供的猜拳表格翻译成二维数组胜负表,通过取模运算在周期数组中定位每次的出拳,再利用胜负表快速判断结果
1. 存储出拳规律
题目输入的第二行和第三行分别给出了小A和小B的出拳规律,我们用两个数组来存储:
- **数组
a[]**:存储小A的出拳周期,长度为na- 例如:
a[0] = 0表示小A第一次出”剪刀” a[1] = 1表示小A第二次出”石头”- 以此类推,小A的出拳会按照这个数组循环
- 例如:
- **数组
b[]**:存储小B的出拳周期,长度为nb- 例如:
b[0] = 0表示小B第一次出”剪刀” b[1] = 3表示小B第二次出”蜥蜴人”- 以此类推,小B的出拳会按照这个数组循环
- 例如:
数组与周期序列的对应关系:
- 小A第
i次出拳:a[i % na]- 当
i = 0时,0 % 6 = 0,出a[0] - 当
i = 6时,6 % 6 = 0,又出a[0],完成一个周期循环
- 当
- 同理,小B第
i次出拳:b[i % nb]
2. 胜负表的构建
题目给出了5种手势的胜负关系,我们用 5×5 的二维数组 来存储所有可能的结果:
score[甲][乙] = 1表示甲赢score[甲][乙] = 0表示平局score[甲][乙] = -1表示甲输(乙赢)
数组下标对应关系:
0→ 剪刀1→ 石头2→ 布3→ 蜥蜴人4→ 斯波克
胜负表解读(以第一行为例):
text
1 | {0, -1, 1, 1, -1} // 甲出剪刀(0)时 |
这样在判断胜负时,只需要查表即可,不需要写复杂的 if-else 判断(其实我第一次做想到的思路就是if-else,现在看确实有更好的做法)。
3. 得分统计
遍历 0 到 N-1 次猜拳:
ia = i % na:计算小A本次出拳在数组a[]中的位置ib = i % nb:计算小B本次出拳在数组b[]中的位置a[ia]和b[ib]就是双方本次的手势- 查表
score[a[ia]][b[ib]]得到结果 - 根据结果累加对应玩家的得分
代码实现
cpp
1 |
|
复杂度分析
- 时间复杂度:
O(N),只需要遍历一次猜拳过程 - 空间复杂度:
O(NA + NB),存储两个周期数组
注意事项
- 数组
a[]和b[]存储的是出拳规律,不是出拳序列本身 - 通过
i % na和i % nb的取模运算实现周期循环 - 胜负表中
score[a[ia]][b[ib]]的查表方式,第一个下标是甲的出拳,第二个是乙的出拳 - 数组
score初始化时每一行用{}括起来,避免负号引起的编译错误