Week9 作业——B - 东东学打牌

题目

最近,东东沉迷于打牌。所以他找到 HRZ、ZJM 等人和他一起打牌。由于人数众多,东东稍微修改了亿下游戏规则:

所有扑克牌只按数字来算大小,忽略花色。
每张扑克牌的大小由一个值表示。A, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K 分别指代 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13。
每个玩家抽得 5 张扑克牌,组成一手牌!(每种扑克牌的张数是无限的,你不用担心,东东家里有无数副扑克牌)

理所当然地,一手牌是有不同类型,并且有大小之分的。
举个栗子,现在东东的 “一手牌”(记为 α),瑞神的 “一手牌”(记为 β),要么 α > β,要么 α < β,要么 α = β。
那么这两个 “一手牌”,如何进行比较大小呢?首先对于不同类型的一手牌,其值的大小即下面的标号;对于同类型的一手牌,根据组成这手牌的 5 张牌不同,其值不同。下面依次列举了这手牌的形成规则:

大牌:这手牌不符合下面任一个形成规则。如果 α 和 β 都是大牌,那么定义它们的大小为组成这手牌的 5 张牌的大小总和。
对子:5 张牌中有 2 张牌的值相等。如果 α 和 β 都是对子,比较这个 “对子” 的大小,如果 α 和 β 的 “对子” 大小相等,那么比较剩下 3 张牌的总和。
两对:5 张牌中有两个不同的对子。如果 α 和 β 都是两对,先比较双方较大的那个对子,如果相等,再比较双方较小的那个对子,如果还相等,只能比较 5 张牌中的最后那张牌组不成对子的牌。
三个:5 张牌中有 3 张牌的值相等。如果 α 和 β 都是 “三个”,比较这个 “三个” 的大小,如果 α 和 β 的 “三个” 大小相等,那么比较剩下 2 张牌的总和。
三带二:5 张牌中有 3 张牌的值相等,另外 2 张牌值也相等。如果 α 和 β 都是 “三带二”,先比较它们的 “三个” 的大小,如果相等,再比较 “对子” 的大小。
炸弹:5 张牌中有 4 张牌的值相等。如果 α 和 β 都是 “炸弹”,比较 “炸弹” 的大小,如果相等,比较剩下那张牌的大小。
顺子:5 张牌中形成 x, x+1, x+2, x+3, x+4。如果 α 和 β 都是 “顺子”,直接比较两个顺子的最大值。
龙顺:5 张牌分别为 10、J、Q、K、A。

作为一个称职的魔法师,东东得知了全场人手里 5 张牌的情况。他现在要输出一个排行榜。排行榜按照选手们的 “一手牌” 大小进行排序,如果两个选手的牌相等,那么人名字典序小的排在前面。
不料,此时一束宇宙射线扫过,为了躲避宇宙射线,东东慌乱中清空了他脑中的 Cache。请你告诉东东,全场人的排名

输入

输入包含多组数据。每组输入开头一个整数 n (1 <= n <= 1e5),表明全场共多少人。

随后是 n 行,每行一个字符串 s1 和 s2 (1 <= |s1|,|s2| <= 10), s1 是对应人的名字,s2 是他手里的牌情况。

输出

对于每组测试数据,输出 n 行,即这次全场人的排名。样例输入3

DongDong AAA109

ZJM 678910

Hrz 678910

样例输出

Hrz

ZJM

DongDong

思路

数据结构

int cnt[14]:存储每个成员手里的牌牌面数字的个数
int num[5]:存储成员中单张牌的牌面数字
int num_cnt[6]:存储成员不同个数牌的个数
struct player:存储每个成员的姓名、牌、牌的类型、牌中呈对、三张、四张的牌的牌面的数字和手中牌面数字之和
{
 char name[20];
// string name;
 int pai[5];
 int type;
 int duizi[2]={0,0};
 int sum; 
 bool operator<(const player &p) const 
 重载比较符,先比较type,在type相等时,再按顺序比较存储在duizi中的数
 其中当为两对的情况时,duizi[0]存储两对中较大的,duizi[1]中存储为两对中较小的
 当为三代二:duizi[0]3个的数字,duizi[1]2个的数字
 当为三个和炸弹时:duizi[0]存储三个或四个的数字,duizi[1]=0
 在其他情况时:duizi[0/1]=0
 当duizi都相等时,根据题意的各个比较都可以转化为比较所有牌面总和
 当牌面总和也相等时,说明两个player手中的牌相同,所以直接比较名字的字典序即可得到排名
 {
  if(type!=p.type)
   return type>p.type;
  if(duizi[0]!=p.duizi[0])
   return duizi[0]>p.duizi[0];
  if(duizi[1]!=p.duizi[1])
   return duizi[1]>p.duizi[1];
  if(sum!=p.sum)
   return sum>p.sum;
  //为啥反了???? 
  int l1=strlen(name),l2=strlen(p.name);
  for(int j=0;j<min(l1,l2);j++)
  {
   if(name[j]==p.name[j]) continue;
   if(name[j]<p.name[j]) return true;
   if(name[j]>p.name[j]) return false;
  }
  if(l1<l2) return true;
  return false;
//  return name<p.name; 
 } 
}p[maxn]

解法

1、对每一个新队员,先将该队员对应的p[i]内的所有内容初始化,再录入姓名到name中,牌面到临时字符串容器中,将其处理成数字并储存到p中,同时计算每个牌面数字的个数存储到cnt中
2、从小到大遍历cnt数组,计算不同个数的数字的个数存储到num_cnt中,将单张牌存储到num中,同时针对不同的cnt更新type
3、判断牌是否是顺子或龙顺并更新type
4、调用sort,按从大到小排序并按排名输出。

错误

1、谁能想到把龙顺以为成从10开始连续的五张牌这个脑残的错误我能调一周???orz
2、注意当用char[20]存储名字的时候,调用return name<p.name时HRZ和ZJM的排序会反过来,所以考虑自己实现字典序的比较,如下:

  //为啥反了???? 
  int l1=strlen(name),l2=strlen(p.name);
  for(int j=0;j<min(l1,l2);j++)
  {
   if(name[j]==p.name[j]) continue;
   if(name[j]<p.name[j]) return true;
   if(name[j]>p.name[j]) return false;
  }
  if(l1<l2) return true;
  return false;
//  return name<p.name; 
 } 
}p[maxn]

但是当名字用string类型存储时,return name<p.name得到的就是按照字典序排序的名字
3、一开始忘记对新一组的数据duizi置为空值
4、注意在进入判断该成员的牌的类型之前先将牌的类型置为1,之后仅对牌更高一级的情况再更新牌的类型
5、五张牌都相等的情况也是炸弹

代码

#include<iostream>
#include<algorithm>
#include<string.h>
#include<string>
using namespace std;
const int maxn=1e5+10;
const int maxm=20;
int cnt[14];
int num[5];
int num_cnt[6];
int tmp_sum=0;
struct player
{
	char name[20];
//	string name;
	int pai[5];
	int type;
	int duizi[2]={0,0};
	int sum;	
	bool operator<(const player &p) const
	{
		if(type!=p.type)
			return type>p.type;
		if(duizi[0]!=p.duizi[0])
			return duizi[0]>p.duizi[0];
		if(duizi[1]!=p.duizi[1])
			return duizi[1]>p.duizi[1];
		if(sum!=p.sum)
			return sum>p.sum;
		//为啥反了????	
		int l1=strlen(name),l2=strlen(p.name);
		for(int j=0;j<min(l1,l2);j++)
		{
			if(name[j]==p.name[j]) continue;
			if(name[j]<p.name[j]) return true;
			if(name[j]>p.name[j]) return false;
		}
		if(l1<l2) return true;
		return false;
//		return name<p.name; 
	}	
}p[maxn];
int main()
{
	int n;
	char temp_pai[20];
	while(scanf("%d",&n)!=EOF)
	{	
		for(int i=0;i<n;i++)//i为第i个人的牌 
		{
			cin>>p[i].name;
			cin>>temp_pai;
			p[i].duizi[0]=0;//注意每次需将p中所有的值置为初值 
			p[i].duizi[1]=0;
			int j=0;
			int m=0;//初始化 
			p[i].sum=0;//初始化 
			memset(cnt,0,sizeof(cnt));
			
			while(temp_pai[j]!='\0')
			{
				if(temp_pai[j]=='A')
				{
					p[i].pai[m]=1;
					p[i].sum+=1;
					cnt[1]++;					
				}
				else if(temp_pai[j]=='J')
				{
					p[i].pai[m]=11;
					p[i].sum+=11;//直接更新该player的sum 
					cnt[11]++;					
				}
				else if(temp_pai[j]=='Q')
				{
					p[i].pai[m]=12;	
					p[i].sum+=12;
					cnt[12]++;				
				}
				else if(temp_pai[j]=='K')
				{
					p[i].pai[m]=13;	
					p[i].sum+=13;
					cnt[13]++;				
				}
				else if(temp_pai[j]=='1')
				{
					p[i].pai[m]=10;
					p[i].sum+=10;
					cnt[10]++;
					j++;
				}
				else
				{
					int tmp=temp_pai[j]-'0';
					p[i].pai[m]=tmp;
					p[i].sum+=tmp;
					cnt[tmp]++;				
				}
				j++;
				m++;
			}
			m=0;//单张牌的个数 
			int x=0;//对子的个数 
			memset(num,0,sizeof(num));//存储仅有一张的牌 
			memset(num_cnt,0,sizeof(num_cnt));
//			cout<<"cnt:"<<endl;
//			for(j=0;j<14;j++)
//				cout<<cnt[j]<<' ';
//			cout<<endl;			
			p[i].type=1;//将初始情况置为1,因为后面可能出现五张牌都相等的情况 
			for(j=1;j<=13;j++)
			{
				if(cnt[j]==1)
				{
					num_cnt[1]++;
					num[m]=j;
					m++;
				}
				else if(cnt[j]==2)
				{
					num_cnt[2]++;
					if(x==1)
					{
						if(num_cnt[2]==2)//注意这里num_cnt已经加一了,故要判断是否==2 
						{
						p[i].duizi[1]=p[i].duizi[0];
						p[i].duizi[0]=j;
						p[i].type=3;						
						}
						else if(num_cnt[3]==1)
						{
							p[i].duizi[x]=j;
							p[i].type=5;
						}
						x++;
					}
					else
					{
					p[i].duizi[x]=j;
					p[i].type=2;						
					}
					x++; 			
				}
				else if(cnt[j]==3)
				{
					num_cnt[3]++;
					if(x==1)
					{
						p[i].duizi[1]=p[i].duizi[0];
						p[i].duizi[0]=j;
						p[i].type=5;
					}
					else
					{
						p[i].duizi[x]=j;
						p[i].type=4;						
					} 
					x++; 
				}
				else if(cnt[j]==4||cnt[j]==5)//注意当五张牌都相等时,也是炸弹 
				{
					num_cnt[4]++;
					p[i].duizi[x]=j;
					p[i].type=6;
					x++;
				}	
			}
//			cout<<"num_cnt:"<<endl;
//			for(j=0;j<6;j++)
//				cout<<num_cnt[j]<<' ';
//			cout<<endl;
//			cout<<"num:"<<endl;
//			for(j=0;j<5;j++)
//				cout<<num[j]<<' ';
//			cout<<endl;
				
			if(num_cnt[1]==5)
			{
				int flag=0;
				for(j=0;j<4;j++)
					if(num[j]!=num[j+1]-1)
						flag=1;
				if(flag==0)
				{
					p[i].type=7;					
				}
				else if(num[0]==1&&num[1]==10&&num[2]==11&&num[3]==12&&num[4]==13)//注意龙顺是10、j、q、k、a 
					p[i].type=8;
			}	
		}
		sort(p,p+n);
		for(int i=0;i<n;i++)
		{
			cout<<p[i].name<<endl;	
//			for(int j=0;j<5;j++)
//				cout<<p[i].pai[j];
//			cout<<p[i].type<<' '<<p[i].duizi[0]<<' '<<p[i].duizi[1]<<' '<<p[i].sum;
//			cout<<endl;		
		}

	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值