【C++】B2112 石头剪子布
文章目录
- 💯前言
- 💯题目描述
- 游戏规则:
- 输入格式:
- 输出格式:
- 输入输出样例:
- 解题分析与实现
- 💯我的做法
- 实现逻辑
- 优点与不足
- 💯老师的做法
- 实现逻辑
- 优点与不足
- 💯对比分析
- 💯优化与扩展
- 优化代码实现
- 优化后的优点
- 扩展场景
- 💯总结
💯前言
- "石头剪子布"是一种经典游戏,它不仅规则简单,还能引发复杂的编程逻辑讨论。在本篇文章中,我们将以一道 C++ 的编程题为例,深入剖析解题的思路、优化方法,以及延伸出的编程概念。通过对比两种实现方式(我的实现和老师的实现),结合扩展性和代码优化的思路,帮助读者全面理解这一题目及其潜在的编程技巧。
C++ 参考手册
💯题目描述
B2112 石头剪子布
石头剪子布,是一种猜拳游戏,起源于中国,然后传到日本,朝鲜等地,随着亚欧贸易的不断发展它传到西欧,到了现代化逐渐国际化的世界中。简单明了的规则,使得石头剪子布没有任何知识和规则漏洞可钻,单次玩法让比赛公平,容易且充满心理博弈,使得石头剪子布这个古老的游戏同时用于“意外”与“技术”两种特性,深受世界人民喜爱。
游戏规则:
石头剪子布,布包石头,石头砸剪刀,剪刀剪布。
现在,需要你写一个程序来判断石头剪子布游戏的结果。
输入格式:
第一行是一个整数 N,表示一共进行 N 次游戏。1 <= N <= 100。
接下来 N 行的每一行包括两个字符串,表示游戏参与者 Player1,Player2 的选择(石头、剪刀或者布):S1,S2。
字符串之间以空格隔开 S1 S2 只可能取值在 [Rock, Scissors, Paper](大小写敏感)中。
输出格式:
输出包括 N 行,每一行对应一个胜利者(Player1 或者 Player2),或者游戏出现平局,则输出 Tie。
输入输出样例:
输入 #1
3
Rock Scissors
Paper Paper
Rock Paper
输出 #1
Player1
Tie
Player2
解题分析与实现
接下来,我们将从两个实现方式出发:我的做法、老师的做法,逐步剖析解决问题的不同方式,最后对比两种实现,并延展出优化和扩展思路。
💯我的做法
以下是我的代码实现:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n;
cin >> n;
string s1, s2;
for(int i = 0; i < n; i++)
{
cin >> s1 >> s2;
if(s1 == "Rock")
{
if(s2 == "Scissors")
cout << "Player1" << endl;
else if(s2 == "Paper")
cout << "Player2" << endl;
else
cout << "Tie" << endl;
}
else if(s1 == "Scissors")
{
if(s2 == "Scissors")
cout << "Tie" << endl;
else if(s2 == "Paper")
cout << "Player1" << endl;
else
cout << "Player2" << endl;
}
else
{
if(s2 == "Scissors")
cout << "Player2" << endl;
else if(s2 == "Paper")
cout << "Tie" << endl;
else
cout << "Player1" << endl;
}
}
return 0;
}
实现逻辑
-
输入处理:读取整数
n
,表示对局次数,循环读取Player1
和Player2
的选择。 -
判断规则:
- 通过嵌套的
if-else
分支,根据Player1
的选择 (Rock
,Scissors
,Paper
) 逐步判断Player2
的选择,从而决定胜负。 - 平局条件 (
s1 == s2
) 被单独处理。
- 通过嵌套的
-
逐行输出:根据每场比赛的结果,输出 “Player1”、“Player2” 或 “Tie”。
优点与不足
-
优点:
- 逻辑清晰,适合初学者。
- 每种情况都显式列出,容易理解。
-
不足:
- 冗余逻辑:大量的条件分支导致代码较为臃肿。
- 可扩展性差:如果加入新规则(如 “Lizard” 和 “Spock”),代码需要大规模改动。
💯老师的做法
以下是老师的代码实现:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int n = 0;
cin >> n;
int i = 0;
string s1;
string s2;
while(n--)
{
cin >> s1;
cin >> s2;
if(s1 == s2)
cout << "Tie" << endl;
else if (s1 == "Rock" && s2 == "Scissors")
cout << "Player1" << endl;
else if (s1 == "Scissors" && s2 == "Paper")
cout << "Player1" << endl;
else if (s1 == "Paper" && s2 == "Rock")
cout << "Player1" << endl;
else
cout << "Player2" << endl;
}
return 0;
}
实现逻辑
- 使用
while
循环减少代码结构的复杂性,每次循环中处理一场比赛。 - 平局条件 (
s1 == s2
) 优先判断,避免进入更多分支。 Player1
的胜利条件通过显式列举的方式判断。- 如果上述条件都不满足,则默认为
Player2
胜出。
优点与不足
-
优点:
- 代码结构简洁,分支层次少。
- 平局条件优先处理,逻辑顺畅。
-
不足:
- 同样存在条件分支冗余问题。
- 可扩展性不足。
💯对比分析
对比点 | 我的做法 | 老师的做法 |
---|---|---|
代码结构 | 使用嵌套 if-else ,分支较多 | 使用单层 if-else ,逻辑更清晰 |
冗余程度 | 条件分支更多,显式判断所有情况 | 条件分支较少,但仍有显式判断 |
可扩展性 | 新规则需要大规模修改 | 新规则需要大规模修改 |
适合初学者 | 逻辑直观,适合初学者练习 | 逻辑简化,更适合掌握基本结构的学生 |
💯优化与扩展
为了进一步优化和扩展,我们可以使用数据结构来简化逻辑,提高可扩展性。
优化代码实现
通过使用 map
存储胜负规则,可以避免显式列举所有情况。
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
int n;
cin >> n;
// 定义胜负规则
map<string, string> winRules = {
{"Rock", "Scissors"},
{"Scissors", "Paper"},
{"Paper", "Rock"}
};
string s1, s2;
while (n--) {
cin >> s1 >> s2;
if (s1 == s2) {
cout << "Tie" << endl;
} else if (winRules[s1] == s2) {
cout << "Player1" << endl;
} else {
cout << "Player2" << endl;
}
}
return 0;
}
优化后的优点
- 逻辑简化:通过查表判断胜负,避免嵌套的
if-else
。 - 易扩展性:增加新规则只需扩展
map
,无需修改核心逻辑。 - 代码简洁:主逻辑更清晰,减少冗余判断。
扩展场景
- 多种新规则:如引入 “Lizard” 和 “Spock”。
- 支持多轮游戏:记录每场比赛的胜利者,并统计最终胜负结果。
- 本地化支持:使用多语言输出结果。
💯总结
通过这道题目,我们探讨了两种不同的解法,以及如何优化代码逻辑。我的实现和老师的实现都能正确解决问题,但在简洁性和扩展性上存在一定不足。优化后的代码通过数据结构简化了逻辑,提高了代码的可维护性和可扩展性。
编程不仅仅是实现功能,还在于如何更优雅、更高效地实现。本题为我们提供了一个非常好的练习机会,希望读者在掌握了这些方法后,能更深入地理解编程的本质和技巧。
1. 理解C++的基础
学习重点:
- 掌握基本语法:
- 学会声明变量、条件语句、循环、函数等核心语法。
- 掌握数组、指针、引用等基础知识。
- 熟悉标准输入输出:
- 掌握
cin
和cout
的用法。 - 理解格式化输出,例如
std::setw
和std::fixed
。
- 掌握
- 记住编译器的角色:
- 理解源代码如何通过编译器转换为可执行程序,选择一款好的IDE(如Visual Studio、Clion、VSCode)。
建议:用大量小程序练习这些基础语法,比如写一个计算器、模拟猜数字游戏等。
2. 深入学习C++的特性
学习重点:
- 面向对象编程(OOP):
- 理解类和对象,熟悉如何定义类、成员变量和成员函数。
- 掌握封装、继承、多态三大核心特性。
- 内存管理:
- 理解指针的用法,掌握动态内存分配(
new
和delete
)。 - 学习如何避免内存泄漏,熟悉智能指针(如
std::unique_ptr
和std::shared_ptr
)。
- 理解指针的用法,掌握动态内存分配(
- 标准模板库(STL):
- 熟悉常用的容器(如
vector
、map
、set
)和算法(如sort
、find
)。 - 掌握迭代器的用法。
- 熟悉常用的容器(如
- 异常处理:
- 学习使用
try-catch
块处理异常。 - 理解异常的用途以及如何设计健壮的代码。
- 学习使用
建议:尝试开发一个小型项目,比如一个学生管理系统,综合应用类、STL、指针和动态内存分配。
3. 掌握进阶内容
学习重点:
- 模板:
- 理解函数模板和类模板,掌握泛型编程思想。
- 学习模板特化和模板元编程的基本概念。
- 多线程与并发:
- 学习 C++11 提供的多线程支持(如
std::thread
)。 - 熟悉互斥锁(
std::mutex
)和条件变量(std::condition_variable
)。
- 学习 C++11 提供的多线程支持(如
- C++与C的兼容性:
- 学习如何在C++中使用C语言代码,理解C和C++的区别。
- 掌握C风格字符串(
char[]
)和C++字符串(std::string
)的转换。
建议:在这一阶段,可以挑战更复杂的项目,比如开发一个小型的HTTP服务器,学习网络编程并结合多线程。
4. 学习资源与实践方法
学习资源:
- 书籍:
- 《C++ Primer》:非常适合初学者的经典书籍。
- 《Effective C++》:进阶学习C++最佳实践的指南。
- 《The C++ Programming Language》:Bjarne Stroustrup(C++之父)的权威著作。
- 在线课程:
- Coursera 上的 C++ 编程课程。
- YouTube 上免费的 C++ 系列教程。
- 社区与文档:
- 参与C++相关的论坛(如CSDN、Stack Overflow)。
- 阅读官方文档(https://en.cppreference.com)。
实践方法:
- 多写代码,多调试:
- 每学一个概念后,写至少两个示例代码并进行调试。
- 做小项目:
- 从简单的控制台程序开始,比如文件读写、计算器、小游戏等。
- 慢慢过渡到图形界面或网络程序开发。
- 阅读他人代码:
- 阅读开源项目的代码,理解优秀代码的设计思路。
- 参加编程比赛:
- 比如 LeetCode 或 Codeforces,可以帮助你提升算法能力和C++的熟练度。
5. 保持耐心与兴趣
学习C++可能会面临以下困难:
- 复杂的语法:如模板、智能指针、多线程等。
- 调试困难:指针错误、内存泄漏、未定义行为可能让人头疼。
如何应对:
- 将大问题拆解成小问题,逐步解决。
- 不断重复基础知识,以加深理解。
- 保持兴趣,尝试一些有趣的项目,比如写一个游戏、制作一个简单的图形程序。
小结
学习C++需要一个循序渐进的过程,从基础语法到面向对象编程,再到进阶的模板和并发编程,每一步都需要耐心和实践。通过充分利用学习资源、进行大量编码练习,以及挑战实际项目,你一定可以成为C++的高手。记住,学习的核心在于理解,而非死记硬背。
祝你学有所成,享受C++编程的乐趣!