PTA-德州扑克 题解

德州扑克,一题工程量巨大的题,属实是个带项目。但本题解简单易懂注释多,应该比较好理解吧

题目如下:

最近,阿夸迷于德州扑克。所以她找到了很多人和她一起玩。由于人数众多,阿夸必须更改游戏规则:

所有扑克牌均只看数字,不计花色。
每张卡的值为1、2、3、4、5、6、7、8、9、10、11、12、13 中的一种(对应A,2、3、4、5、6、7, 8、9、10,J,Q,K)
每位玩家从一副完整的扑克牌(没有大小王)中抽出五张扑克牌,可能出现的手牌的值从低到高排列如下:

高牌:不包含以下牌的牌。对于都是高牌的牌,按照五张牌的值的和进行从大到小排序。
对子:手中的5张牌中有2张相同值的牌。对于都拥有对子的牌,按构成该对子的牌的值进行从大到小地排序。如果这些都相同,则按手牌中余下3张牌的值的和进行从大到小排序。
两对:手中拥有两对不同的对子。对于都包含两对的手牌,按其最高对子的值进行从大到小排序。如果最高对子相同,则按另一个对子的值从大到小地进行排序。如果这些值相同,则按剩余牌的值从大到小地进行排序。
三条:手中拥有3张相同值的牌。对于都包含三条的手牌按构成三条的牌的值进行从大到小地排序。如果这些值相同,则按剩余牌的值从大到小地进行排序。
满堂红:手中拥有一个三条和一个对子。同理,先按三条大小排序,如果三条大小相同,则按对子大小进行排序。
四条:手中拥有4张相同值的牌。对于都包含四条的手牌按构成四条的牌的值进行从大到小地排序。如果这些值相同,则按剩余牌的值从大到小地进行排序。
顺子:手中拥有5张连续值的卡。对于都包含顺子的手牌按顺子最大的牌进行排序。
皇家同花顺:手中拥有10到A(10、J、Q、K、A)。是最大的手牌!

现在,阿夸已经知道了每个人的手牌,她想要知道所有人的排名列表。如果玩家的手牌大小相等,则按玩家名字的字典序输出。保证没有重复的名字。你能帮帮她吗?

输入格式:

第一行包含一个正整数 N (1<=N<=100000) ,表示玩家的人数。

接下来 N 行,每行包含两个字符串:m (1<=|m|<=10 ) ,表示玩家的名字;s (1<=|s|<=10),表示玩家的手牌。

输出格式:

输出 N个玩家的排名列表。

输入样例:

3
Alice AAA109
Bob 678910
Boa 678910

输出样例:

Boa
Bob
Alice

解:

头文件

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;

本题包含一个结构体struct People、三个函数:①int trans(string str)②void deal_the_level_of(People x,int cur)③int cmp(People a,People b)、主函数:

struct People
{
    string name;
    string s;
    int card[14];
    double level;//大等级从1-8  细分等级顺序用小数点来算大小  
}f[100005];

结构体的成员很简单:名字和手牌都为string类,方便输入输出和拆分;card数组分别储存这个人拥有的牌的情况;最后一个最重要的等级,我用了double型来定义,通过小数点来判定牌面大小的优先级;同时定义一个全局变量的数组方便后续输入和处理。

int trans(string str)
{
    if(str=="A")return 1;
    if(str=="2")return 2;
    if(str=="3")return 3;
    if(str=="4")return 4;
    if(str=="5")return 5;
    if(str=="6")return 6;
    if(str=="7")return 7;
    if(str=="8")return 8;
    if(str=="9")return 9;
    if(str=="10")return 10;
    if(str=="J")return 11;
    if(str=="Q")return 12;
    if(str=="K")return 13;
}

trans函数的功能想必大家都不陌生,之前的白话文编程作业多次需要将字符串转化为有数值的数字,这个函数也是一个将string类的字符串转化为数字,即对应的牌面。

int cmp(People a,People b)
{
    if(a.level!=b.level)return a.level>b.level;
    else return a.name<b.name;
} 

这个cmp(compare)函数是给后面要用的sort函数作为其第三个参数,说白了就是告诉sort怎么进行排序。本题是按照牌面从大到小来排,如果牌面相同就按照名字的字典序来排。由于本题用的是string类,进行字典序排序十分简单,一个小于号“<”就搞定。

下面是本题最重要的函数!!由于这个函数很长,有70多行,所以将解释放入注释中。

函数顾名思义--deal the level of (某个People型的变量):处理People型变量x的等级。

void deal_the_level_of(People x,int cur)    //current 当前的那个下标 
{
    int single_value=0;//剩下单张牌总值的和 
    int couple1=0,couple2=0;//有一个对子 、有两个对子 其值为对子对应的牌面 
    int three=0; //判断是否有三条 
    int four=0; //判断是否有四条 
    bool line=false;//判断是否有顺子 有的话更为true 
    int maxn;//顺子中最大的牌 

    for(int i=1;i<=13;i++)
    {
        if(x.card[i]==1)//遇到这张牌是单张的 
        {
            int judge=1,j;//判断顺子 
            for(j=i+1;j<=i+4;j++)judge*=x.card[j];//不断相乘,只有全都是 1 ,乘出来才会为1. 
            if(judge==1){//是顺子 
                line=true;
                maxn=j-1;
                break;
            }
            else single_value+=i;
        }
        if(x.card[i]==2)//如果这张牌拥有两张 
        {
            if(couple1==0)couple1=i;//如果之前没出现过对子 
            else couple2=i;//之前出现过对子了,那这个就是第二个对子 
        }
        if(x.card[i]==3)three=i;//如果这张牌拥有三张 
        if(x.card[i]>=4)//这张牌拥有四张或五张,一样的都归为 四条  这个级别。 
        {
            four=i;
            if(x.card[i]==5)single_value+=i;//五张一样的 剩下那张归为单张 
        }
    }
/*
注意!如果一个大等级中,有多个排序的优先级
那么:
第一优先级 乘 0.01
第二优先级    0.0001
第三优先级    0.000001
因为最大的单张牌面“K”的值为13,超过10了,如果少乘一个0会影响到大等级。 
*/ 
    if(x.card[10]*x.card[11]*x.card[12]*x.card[13]*x.card[1]==1)x.level+=8.0;//皇家同花顺 ,永远滴神! 
    else if(line)//顺子  第7级 
    {
        x.level=7.0;
        x.level+=maxn*0.01;
    }
    else if(four)//四条 第6级 
    {
        x.level=6.0;
        x.level+=four*0.01+single_value*0.0001;
    }
    else if(three)//满堂红或三条 
    {
        if(couple1){//有一个对子,满堂红  第5级 
            x.level=5.0;
            x.level+=three*0.01+couple1*0.0001; 
        }
        else{//三条 第4级 
            x.level=4.0; 
            x.level+=three*0.01+single_value*0.0001;
        }
    }
    else if(couple1)//两对或对子 
    {
        if(couple2){//两对  第3级 
            x.level=3.0;
            x.level+=couple2*0.01+couple1*0.0001+single_value*0.000001;
        }
        else{//单个对子  第2级 
            x.level=2.0;
            x.level+=couple1*0.01+single_value*0.0001;
        }
    }
    else {//高牌  最弱的第1级 
        x.level=1.0;
        x.level+=single_value*0.0001;
    }
    f[cur]=x;//不用指针,将其转移给当前的这个f (因为f数组是全局变量) ,不然f数组的值不会改变的 
}

主函数:

解释还是放在注释里啦!

int main()
{
    int n,value;
    string tmp;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>f[i].name>>f[i].s;
        string str=f[i].s;
        for(int j=0,t=0;t<5;t++)//共五张牌 
        {
            if(str[j]!='1'){ //不为10 
                tmp=str[j];  //提取出这个字符 
                value=trans(tmp);//转换这个字符为对应的值 
                f[i].card[value]++;//该值的手牌数+1 
                j++;
            }
            else{   //遇到10了,只有 10 才会出现 1这个数字,因为牌值为1的是 A 
                f[i].card[10]++;
                j+=2;   //跳两格           
            }
        }
        deal_the_level_of(f[i],i);//处理f[i],记得带下标i 
    }
    sort(f,f+n,cmp);    //排序 
    for(int i=0;i<n;i++)cout<<f[i].name<<endl;  //输出 
    return 0;
}

完整代码:

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
struct People
{
    string name;
    string s;
    int card[14];
    double level;//大等级从1-8  细分等级顺序用小数点来算大小  
}f[100005];
int trans(string str)
{
    if(str=="A")return 1;
    if(str=="2")return 2;
    if(str=="3")return 3;
    if(str=="4")return 4;
    if(str=="5")return 5;
    if(str=="6")return 6;
    if(str=="7")return 7;
    if(str=="8")return 8;
    if(str=="9")return 9;
    if(str=="10")return 10;
    if(str=="J")return 11;
    if(str=="Q")return 12;
    if(str=="K")return 13;
}
int cmp(People a,People b)
{
    if(a.level!=b.level)return a.level>b.level;
    else return a.name<b.name;
} 
void deal_the_level_of(People x,int cur)//current 当前的那个下标 
{
    int single_value=0;//剩下单张牌总值的和 
    int couple1=0,couple2=0;//有一个对子 、有两个对子 其值为对子对应的牌面 
    int three=0; //判断是否有三条 
    int four=0; //判断是否有四条 
    bool line=false;//判断是否有顺子 有的话更为true 
    int maxn;//顺子中最大的牌 

    for(int i=1;i<=13;i++)
    {
        if(x.card[i]==1)//遇到这张牌是单张的 
        {
            int judge=1,j;//判断顺子 
            for(j=i+1;j<=i+4;j++)judge*=x.card[j];//不断相乘,只有全都是 1 ,乘出来才会为1. 
            if(judge==1){//是顺子 
                line=true;
                maxn=j-1;
                break;
            }
            else single_value+=i;
        }
        if(x.card[i]==2)//如果这张牌拥有两张 
        {
            if(couple1==0)couple1=i;//如果之前没出现过对子 
            else couple2=i;//之前出现过对子了,那这个就是第二个对子 
        }
        if(x.card[i]==3)three=i;//如果这张牌拥有三张 
        if(x.card[i]>=4)//这张牌拥有四张或五张,一样的都归为 四条  这个级别。 
        {
            four=i;
            if(x.card[i]==5)single_value+=i;//五张一样的 剩下那张归为单张 
        }
    }
/*
注意!如果一个大等级中,有多个排序的优先级
那么:
第一优先级 乘 0.01
第二优先级    0.0001
第三优先级    0.000001
因为最大的单张牌面“K”的值为13,超过10了,如果少乘一个0会影响到大等级。 
*/ 
    if(x.card[10]*x.card[11]*x.card[12]*x.card[13]*x.card[1]==1)x.level+=8.0;//皇家同花顺 ,永远滴神! 
    else if(line)//顺子  第7级 
    {
        x.level=7.0;
        x.level+=maxn*0.01;
    }
    else if(four)//四条 第6级 
    {
        x.level=6.0;
        x.level+=four*0.01+single_value*0.0001;
    }
    else if(three)//满堂红或三条 
    {
        if(couple1){//有一个对子,满堂红  第5级 
            x.level=5.0;
            x.level+=three*0.01+couple1*0.0001; 
        }
        else{//三条 第4级 
            x.level=4.0; 
            x.level+=three*0.01+single_value*0.0001;
        }
    }
    else if(couple1)//两对或对子 
    {
        if(couple2){//两对  第3级 
            x.level=3.0;
            x.level+=couple2*0.01+couple1*0.0001+single_value*0.000001;
        }
        else{//单个对子  第2级 
            x.level=2.0;
            x.level+=couple1*0.01+single_value*0.0001;
        }
    }
    else {//高牌  最弱的第1级 
        x.level=1.0;
        x.level+=single_value*0.0001;
    }
    f[cur]=x;//不用指针,将其转移给当前的这个f (因为f数组是全局变量) ,不然f数组的值不会改变的 
}

int main()
{
    int n,value;
    string tmp;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        cin>>f[i].name>>f[i].s;
        string str=f[i].s;
        for(int j=0,t=0;t<5;t++)//共五张牌 
        {
            if(str[j]!='1'){ //不为10 
                tmp=str[j];  //提取出这个字符 
                value=trans(tmp);//转换这个字符为对应的值 
                f[i].card[value]++;//该值的手牌数+1 
                j++;
            }
            else{   //遇到10了,只有 10 才会出现 1这个数字,因为牌值为1的是 A 
                f[i].card[10]++;
                j+=2;   //跳两格           
            }
        }
        deal_the_level_of(f[i],i);//处理f[i],记得带下标i 
    }
    sort(f,f+n,cmp);    //排序 
    for(int i=0;i<n;i++)cout<<f[i].name<<endl;  //输出 
    return 0;
}

好难TOT

猜你喜欢

转载自www.cnblogs.com/LiangYC1021/p/12357556.html