题目
最近,东东沉迷于打牌。所以他找到 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;
}