🔍 2025蓝桥杯备赛Day3——B2112 石头剪子布
🚀 题目速览
题目难度:⭐️ 适合编程新手掌握条件分支与字典应用
考察重点:逻辑判断、数据结构应用、多组数据处理
Day3——B2112 石头剪子布
题目描述
石头剪子布,是一种猜拳游戏。起源于中国,然后传到日本、朝鲜等地,随着亚欧贸易的不断发展它传到了欧洲,到了近现代逐渐风靡世界。简单明了的规则,使得石头剪子布没有任何规则漏洞可钻,单次玩法比拼运气,多回合玩法比拼心理博弈,使得石头剪子布这个古老的游戏同时用于“意外”与“技术”两种特性,深受世界人民喜爱。
游戏规则:石头打剪刀,布包石头,剪刀剪布。
现在,需要你写一个程序来判断石头剪子布游戏的结果。
输入格式
第一行是一个整数 N N N,表示一共进行了 N N N 次游戏。 1 ≤ N ≤ 100 1 \le N \le 100 1≤N≤100。
接下来
N
N
N 行的每一行包括两个字符串,表示游戏参与者 Player1,Player2 的选择(石头、剪子或者是布):
S1,S2
字符串之间以空格隔开 S1 S2 只可能取值在 Rock
,Scissors
,Paper
(大小写敏感)中。
输出格式
输出包括
N
N
N 行,每一行对应一个胜利者(Player1 或者 Player2),或者游戏出现平局,则输出 Tie
。
输入输出样例 #1
输入 #1
3
Rock Scissors
Paper Paper
Rock Paper
输出 #1
Player1
Tie
Player2
🔥 解法一:字典映射法(扩展性优)
🛠️ 实现思路
核心技巧:建立胜负关系哈希表实现快速查询
算法优势:时间复杂度O(1)单次判断,规则变更时只需修改字典
#include <iostream>
#include <unordered_map>
using namespace std;
int main() {
int N;
cin >> N;
unordered_map<string, string> win_rules = {
{"Rock", "Scissors"}, // Rock击败Scissors
{"Scissors", "Paper"}, // Scissors击败Paper
{"Paper", "Rock"} // Paper击败Rock
};
while (N--) {
string s1, s2;
cin >> s1 >> s2;
if (s1 == s2) {
cout << "Tie" << endl;
} else {
cout << (s2 == win_rules[s1] ? "Player1" : "Player2") << endl;
}
}
return 0;
}
关键知识点
- 哈希表快速查询:通过键值对直接获取胜负关系
- 三元表达式优化:简化条件分支写法
- 规则集中管理:所有胜负关系存储于单一数据结构
🔥 解法二:条件分支法(代码量少)
🛠️ 实现思路
核心技巧:通过首字母简化判断逻辑
代码特点:直接比较首字母,减少字符串操作开销
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
while (n--) {
string a, b;
cin >> a >> b;
if (a[0] == b[0]) {
cout << "Tie\n";
}
else if (a[0] == 'R') {
cout << (b[0] == 'P' ? "Player2\n" : "Player1\n");
}
else if (a[0] == 'S') {
cout << (b[0] == 'R' ? "Player2\n" : "Player1\n");
}
else if (a[0] == 'P') {
cout << (b[0] == 'S' ? "Player2\n" : "Player1\n");
}
}
return 0;
}
💡 关键改进点
- 语法修正:统一使用单引号进行字符比较
- 结构优化:使用三元表达式替代嵌套if-else
- 输入方式:
while(n--)
循环更符合C++习惯
📚 知识点总结
一、哈希表(unordered_map)
-
底层原理 基于哈希函数将键映射到存储桶,通过链表处理冲突。 特性:
- 平均查询时间复杂度O(1)
- 键唯一且无序存储
-
胜负规则定义
unordered_map<string, string> win_rules = { {"Rock", "Scissors"}, // 键:玩家1选择,值:击败的对象 {"Scissors", "Paper"}, {"Paper", "Rock"} };
- 查询逻辑:若
s2 == win_rules[s1]
,说明玩家1的选择击败了玩家2。
- 查询逻辑:若
-
扩展应用
// 处理多对多关系(如新增手势) unordered_map<string, set<string>> win_rules = { {"Rock", {"Scissors", "Lizard"}} };
二、三元条件运算符
-
语法结构
条件 ? 表达式1 : 表达式2
-
示例:
cout << (s2 == win_rules[s1] ? "Player1" : "Player2");
-
-
关键细节
- 表达式1和表达式2的类型必须一致
- 优先级低于比较运算符,需用括号包裹条件部分
三、首字母比较法
-
实现原理 利用字符串首字母快速判断类型:
if (a[0] == 'R') { // 'R'代表Rock // 判断逻辑... }
-
注意事项
- 仅适用于输入严格符合规范的情况(如
Rock
而非rock
) - 新增手势需修改所有分支(如
Lizard
需判断'L'
)
- 仅适用于输入严格符合规范的情况(如
四、循环与输入优化
-
循环结构选择
while (n--) { ... } // 比for循环更简洁,减少变量占用
-
输入批量读取
vector<pair<string, string>> records(N); for (auto& p : records) cin >> p.first >> p.second;
📊 双解法对比分析
维度 | 字典映射法 | 条件分支法 |
---|---|---|
时间复杂度 | O(1) 每次查询 | O(1) |
扩展性 | 高(仅修改字典) | 低(需修改分支逻辑) |
可读性 | ★★★★★(规则可视化) | ★★★☆☆(需推导逻辑) |
内存占用 | 约30字节(哈希表存储) | 无额外存储 |
抗错能力 | 自动处理所有合法输入 | 依赖输入首字母正确性 |
🚨 常见错误警示
- 首字母陷阱
// 错误写法:双引号表示字符串
if (b[0] == "R") → if (b[0] == 'R') // 正确
- 魔法值风险
// 建议添加注释说明字符含义
else if (a[0] == 'R') // R代表Rock
🌟 举一反三
变种题1:扩展手势类型
// 增加蜥蜴和史波克
unordered_map<string, set<string>> win_rules = {
{"Rock", {"Scissors", "Lizard"}},
{"Scissors", {"Paper", "Lizard"}},
{"Paper", {"Rock", "Spock"}},
// 其他规则...
};
变种题2:三局两胜制
int p1_score = 0, p2_score = 0;
// 每局判断逻辑...
cout << (p1_score > p2_score ? "Player1" : "Player2");
🛠️ 实战技巧
- 输入校验增强
// 验证输入合法性
if (a != "Rock" && a != "Scissors" && a != "Paper") {
cout << "Invalid input!";
continue;
}
- 性能优化技巧
// 预先读取所有输入
vector<pair<string, string>> records(N);
for (auto& [a, b] : records) cin >> a >> b;
蓝桥杯考场策略:
- 规则固定且简单 → 优先选择解法二(编码速度快)
- 题目存在扩展可能 → 必选解法一(易维护性优)
- 遇到非法输入处理 → 建议添加校验代码(即使题目未要求)
👉 思考题:若改为玩家2先出拳,如何修改判断逻辑?欢迎在评论区分享你的解法!
(答案提示:交换s1
和s2
的角色,或修改字典键值对方向。)