1. 项目概述:PTA B1018 锤子剪刀布
PTA(Programming Teaching Assistant)平台上的B1018题是一道经典的编程练习题,要求模拟"锤子剪刀布"游戏的胜负判定逻辑。这道题看似简单,但涉及算法设计中的多个核心概念,包括输入输出处理、条件判断、循环控制以及统计计算等基础编程技能。
在实际教学中,这道题常被用作编程入门者的第一个综合性练习。它不仅考察学生对基础语法的掌握程度,更能训练逻辑思维能力和问题分解能力。通过完成这个项目,编程新手可以建立起对程序流程控制的直观理解,为后续学习更复杂的算法打下坚实基础。
2. 核心需求解析
2.1 题目要求拆解
题目要求程序能够:
- 接收多轮游戏的出拳记录
- 统计双方的胜负情况
- 输出最终的胜负统计和各自最常用的获胜手势
具体来说,输入会给出游戏轮数N,随后N行每行给出甲乙双方的手势(C代表锤子、J代表剪刀、B代表布)。程序需要统计双方的胜利次数、平局次数,并找出各自获胜时使用最多的手势(如果次数相同则按字母序选择)。
2.2 算法设计要点
解决这个问题的关键在于:
- 正确实现游戏规则判断逻辑
- 高效统计各类数据
- 处理特殊情况(如相同次数的手势选择)
游戏规则可以简化为:
- 锤子(C)胜剪刀(J)
- 剪刀(J)胜布(B)
- 布(B)胜锤子(C)
3. 实现方案与代码解析
3.1 基础版本实现
n = int(input()) a_win = b_win = draw = 0 a_gesture = {'B':0, 'C':0, 'J':0} b_gesture = {'B':0, 'C':0, 'J':0} for _ in range(n): a, b = input().split() if a == b: draw += 1 elif (a == 'C' and b == 'J') or (a == 'J' and b == 'B') or (a == 'B' and b == 'C'): a_win += 1 a_gesture[a] += 1 else: b_win += 1 b_gesture[b] += 1 print(a_win, draw, b_win) print(b_win, draw, a_win) def get_max(gesture): max_count = max(gesture.values()) return min([k for k,v in gesture.items() if v == max_count]) print(get_max(a_gesture), get_max(b_gesture))3.2 优化版本实现
def main(): n = int(input()) rules = {'C':'J', 'J':'B', 'B':'C'} # 定义胜负关系 a_stats = {'B':0, 'C':0, 'J':0} b_stats = {'B':0, 'C':0, 'J':0} result = [0, 0, 0] # 甲胜、平、乙胜 for _ in range(n): a, b = input().split() if a == b: result[1] += 1 elif rules[a] == b: result[0] += 1 a_stats[a] += 1 else: result[2] += 1 b_stats[b] += 1 print(' '.join(map(str, result[:2] + [result[2]]))) print(' '.join(map(str, [result[2], result[1], result[0]]))) def get_favorite(stat): max_val = max(stat.values()) return min(k for k in stat if stat[k] == max_val) print(get_favorite(a_stats), get_favorite(b_stats)) if __name__ == '__main__': main()4. 关键技术与实现细节
4.1 胜负判断优化
原始版本使用多重条件判断,可读性较差。优化版本采用字典存储胜负关系,使代码更加简洁清晰:
rules = {'C':'J', 'J':'B', 'B':'C'} # 键胜值这样判断胜负时只需检查rules[a] == b即可,大大简化了逻辑。
4.2 统计结果处理
统计最常用手势时需要注意:
- 先找到最大值
- 收集所有等于最大值的键
- 按字母序选择最小的一个
def get_favorite(stat): max_val = max(stat.values()) return min(k for k in stat if stat[k] == max_val)这种实现方式比排序更高效,特别是当手势种类增多时。
4.3 输入输出处理
PTA平台对输入输出格式要求严格,需要注意:
- 使用
input().split()读取每行输入 - 输出时注意空格分隔和顺序
- 最后一行输出不要有多余空格
5. 测试用例与边界情况
5.1 常规测试用例
输入:
10 C J J B C B B B B C J C C C J J B J C J预期输出:
5 3 2 2 3 5 C B5.2 边界情况测试
- 最小输入测试(N=1)
- 全平局情况
- 一方全胜情况
- 手势次数完全相同情况
6. 性能优化与扩展思考
6.1 时间复杂度分析
算法的时间复杂度为O(N),空间复杂度为O(1)(使用固定大小的字典存储统计结果),已经是最优解。
6.2 可能的扩展方向
- 增加游戏历史记录功能
- 实现AI对战模式
- 添加图形界面
- 支持更多手势类型(如石头剪刀布蜥蜴史波克)
7. 常见错误与调试技巧
7.1 常见错误类型
- 胜负判断逻辑错误
- 统计结果未初始化
- 输出格式不符合要求
- 手势次数相同时未按字母序处理
7.2 调试建议
- 先编写胜负判断的单元测试
- 打印中间结果验证统计是否正确
- 使用小规模测试数据逐步调试
- 特别注意边界情况处理
8. 工程实践建议
在实际工程项目中实现类似功能时,建议:
- 使用枚举类型代替字符串表示手势
- 将游戏规则抽象为配置文件
- 实现游戏记录持久化功能
- 添加输入验证和异常处理
例如,可以定义手势枚举:
from enum import Enum class Gesture(Enum): ROCK = 'C' SCISSORS = 'J' PAPER = 'B'这样能提高代码的可读性和可维护性。