W3 school 菜鸟教程 我要自学网 信息学奥赛NOI 花哥的博客 不逼自己一把,怎知自己有多优秀——校长语录

一本通网站1132:石头剪子布

【题目描述】
石头剪子布,是一种猜拳游戏。起源于中国,然后传到日本、朝鲜等地,随着亚欧贸易的不断发展它传到了欧洲,到了近现代逐渐风靡世界。
简单明了的规则,使得石头剪子布没有任何规则漏洞可钻,单次玩法比拼运气,多回合玩法比拼心理博弈,使得石头剪子布这个古老的游戏
同时用于“意外”与“技术”两种特性,深受世界人民喜爱。 游戏规则:石头打剪刀,布包石头,剪刀剪布。 现在,需要你写一个程序来判断石头剪子布游戏的结果。 【输入】 第一行是一个整数N,表示一共进行了N次游戏。1 ≤ N ≤ 100。 接下来N行的每一行包括两个字符串,表示游戏参与者Player1,Player2的选择(石头、剪子或者是布): S1 S2 字符串之间以空格隔开S1,S2只可能取值在{"Rock", "Scissors", "Paper"}(大小写敏感)中。 【输出】 输出包括N行,每一行对应一个胜利者(Player1或者Player2),或者游戏出现平局,则输出Tie。 【输入样例】 3 Rock Scissors Paper Paper Rock Paper 【输出样例】 Player1 Tie Player2

  分析:这个题目难度不大,完全可以暴力解决,如果完全无脑算法也还可以接受,最多9种对决情况。暴力列出9种对决情况的算法在这里就不啰嗦了,如果从结题判断,那最终只有三种结果,故可以暴力列举三种结果,当然,每一种结果仍然可能包含多种情况。在这三种情况中,平局最简单——两个选手出相同手势(其实这里面包含了三种情况,但判断语句都一样),再列出一种,另一种直接else就好。比如,列举Player1胜Player2,那就需要列出三种对决的完整情形。可得到如下代码:

//1132:石头剪子布第一版本:完整暴力列举 
#include<iostream>
using namespace std;
int main()
{
    int n;
    string p1,p2;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        if(p1=="Rock"&&p2=="Scissors"||p1=="Scissors"&&p2=="Paper"||p1=="Paper"&&p2=="Rock")
        {
            cout<<"Player1\n";
        }
        else if(p1==p2)
        {
            cout<<"Tie\n";
        }
        else cout<<"Player2\n";
    }
     return 0;
}
第一版本

  在第一版本里可以做一点小优化,我们注意到三个单词的首字母不用,故我们也可以直接用首字母判断即可,这样可避免字母太多,出现拼写错误。于是有了第二版本:

//1132:石头剪子布第二版本-部分暴力列举 
#include<iostream>
using namespace std;
int main()
{
    int n;
    char p1[10],p2[10];
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        if(p1[0]=='R'&&p2[0]=='S'||p1[0]=='S'&&p2[0]=='P'||p1[0]=='P'&&p2[0]=='R')
        {
            cout<<"Player1\n";
        }
        else if(p1[0]==p2[0])
        {
            cout<<"Tie\n";
        }
        else cout<<"Player2\n";
    }
     return 0;
}
第二版本

  对于C++编程,我们还要学会模块化编程思想,即可以把一些特定的功能编成一个函数,这些可以提高代码的利用率,也可以增加程序的可读性和减少程序维护代价。对本题而言,我们完全可以把判断过程写成一个函数,这样可以大大减少主程序的复杂度,特别是在以后程序变得更加复杂后,模块化是必然趋势。于得得到第三版本:

//1132:石头剪子布第三版本-普通函数 
#include<iostream>
using namespace std;
string rsp(string p1,string p2)
{
    string rerult;
    if(p1[0]=='R'&&p2[0]=='S'||p1[0]=='S'&&p2[0]=='P'||p1[0]=='P'&&p2[0]=='R')
    {
        rerult="Player1\n";
    }
    else if(p1==p2)
    {
        rerult="Tie\n";
    }
    else rerult="Player2\n";
    return rerult;            
 } 
int main()
{
    int n;
    string p1,p2;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        cout<<rsp(p1,p2);
    }
     return 0;
}
第三版本

  如果,我们对这一个问题再做一个更深入的思考,不难发现,P1胜P2的情况很有规律性,如果把石头、剪刀、布放在一个圆圈上,按照一定的方向便是胜/败的判断依据。这里的“圆圈”实质上可以理解为周期性出现。最终结果只有三种,那我们可以想到任何一个整数除以3的余数也只有三种情况(0、1、2),那是否可以考虑对石头、剪刀、布数字化,依次赋值为1,2,3。那我们不难得出:p1胜p2<==>p1的对应值-p2对应值=-1或2,从余数角度完全可以是同一个。为了解决负数问题,我们先加一个3,再对3取余数,那-1和2是等价的了,都将转换成2。至此,程序便可以修改为第四版本:

//1132:石头剪子布第四版本-赋值函数 
#include<iostream>
using namespace std;
int rsp(string s)
{
    if(s[0]=='R') return 1;
    if(s[0]=='S')return 2;
    return 3;
}
int main()
{
    int n;
    string p1,p2;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        if(p1==p2)cout<<"Tie\n";
        else if((rsp(p2)-rsp(p1)+3)%3==1)cout<<"Player1\n";
        else cout<<"Player2\n";
    }
     return 0;
}
第四版本

  再对照上例中(rsp(p2)-rsp(p1)+3)%3这一个表达式可能出现的值为0、1、2,而我们最终结果也正好是三种,那我们可以把三种结果存入数组,用(rsp(p2)-rsp(p1)+3)%3做下标,直接调用结果,于是便有第五版本:

//1132:石头剪子布第五版本-数组函数 
#include<iostream>
using namespace std;
int rsp(string s)
{
    if(s[0]=='R') return 1;
    if(s[0]=='S')return 2;
    return 3;
}
int main()
{
    int n;
    string p1,p2,rerult[3]={"Tie\n","Player1\n","Player2\n"};
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        cout<<rerult[(rsp(p2)-rsp(p1)+3)%3];
    }
     return 0;
}
第五版本

  当然,随着我们学习的深入,我们发现,“数字化”的想法已经不是我们首创,这些想法C++已经都想到了,并作出了很系统的模板封装好,我们只需直接调用便是——那就是功能强大的STL库函数。在这个题中的自编函数可用map来解决,看第六版本:

//1132:石头剪子布第六版本-STL库函数 
#include<iostream>
#include<map> 
using namespace std;
int main()
{
    int n;
    string p1,p2;
    string rerult[3]={"Tie\n","Player1\n","Player2\n"};;
    map<string,int> rsp;
    rsp["Rock"]=1;
    rsp["Scissors"]=2;
    rsp["Paper"]=3;
    cin>>n;
    for(int i=1;i<=n;i++) 
    {
        cin>>p1>>p2;
        cout<<rerult[(rsp[p2]-rsp[p1]+3)%3];
    }
     return 0;
}
第六版本

 

  写这么多,主要想让新学者体验一个程序的修改过程、优化过程,高手们也请多多指教,以便让我和新手们一起学学。

posted @ 2020-05-24 21:21  耍人  阅读(1745)  评论(0编辑  收藏  举报