• PTA-德州扑克 题解


    于2020/02/24记录。

    德州扑克属实是个带难题。本题解简单易懂,命名合理,应该比较好理解

    题目如下:

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

    所有扑克牌均只看数字,不计花色。
    每张卡的值为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;
    }
    

    我遇到的问题:

    细节实在太多了,大量的分类情况堆在一起会十分混乱,所以在编写时最好用笔在一张草稿纸写出分类、优先级排序的思路。
    一共有10个测试点,我的过程为:
    通过0个点->通过第1、2个点:修正了“两对”这个情况的bug。
    错误代码为

    		if(x.card[i]==2)//如果这张牌拥有两张 
    		{
    			if(couple1==0)couple1=i;//如果之前没出现过对子 
    			if(couple1)couple2=i;//之前出现过对子了,那这个就是第二个对子 
    		}
    

    写了两个if语句,问题在于两条语句的顺序错了。执行完第一句后,couple1已经被赋值了,第二句他必不可能为0,所以第二句也会被执行,从而couple2会变成和couple1一样的值。
    正确代码应为

    		if(x.card[i]==2)//如果这张牌拥有两张 
    		{
    			if(couple1==0)couple1=i;//如果之前没出现过对子 
    			else couple2=i;//之前出现过对子了,那这个就是第二个对子 
    		}
    

    或者将两句顺序调换。
    通过2个点->AC:修正了满堂红和三条的bug,我原先没判断三子的大小直接判断了后面剩余牌的大小,此块优先级应该为:先比较三子大小、后比较剩余两张牌的大小。
    错误代码为

    	else if(three)//满堂红或三条 
    	{
    		if(couple1){//有一个对子,满堂红  第5级 
    			x.level=5.0;
    			x.level+=couple1*0.01; 
    		}
    		else{//三条 第4级 
    			x.level=4.0; 
    			x.level+=single_value*0.01;
    		}
    	}
    

    正确代码应为

    	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;
    		}
    	}
    

    希望能对大家有所帮助:)

    完整代码:

    #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

  • 相关阅读:
    apache的日志切割
    实现HTTPS--Apache+Openssl
    CentOS 6.x 编译安装LAMP
    apache的域名跳转
    模型生成过程中检测到一个或多个验证错误
    电商时代已经要过去了。接下来是零售
    电商时代已经要过去了。接下来是零售
    华为手机怎么安装Google
    华为手机怎么安装Google
    table不让td中文字溢出操作方法
  • 原文地址:https://www.cnblogs.com/LiangYC1021/p/12357556.html
Copyright © 2020-2023  润新知