Poker Hands (比较两手牌的大小)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=111
题目描述:这道题要求比较两手牌的大小。每手牌都有5张牌组成,牌的大小的定义如下,首先确定这组牌的所属种类是下面哪一种(若同属同一种类,则根据该分类后面的比较规则继续比较,所属种类越靠后牌越大)。
● High Card:杂牌(不属于下面任何一种)。根据牌从大到小的顺序依次比较。
● Pair:有一对,加3张杂牌组成。先比较对的大小,再从大到小的顺序比较杂牌。
● Two Pairs:有两对,加1帐杂牌。先从大到小比较对的大小,再比较杂牌。
● Three of a Kind:有3张值相同的牌。比较这个值即可。
● Straingt:一条龙。即5张牌连续。比较最大的一张牌即可。
● Flush:清一色。即5张牌花色相同。和杂牌一样比较。
● Full House:3张值相同的牌,加上一对。比较三张相同的值即可。
● Four of a kind:有4张牌相同,即相当于一副“炸弹”。
● Straight flush:同花顺。即5张牌花色相同,并且连续。例如同花色的34567。
题目输入多行,每一行含有10张牌,前五张属于Black,后五张属于White,每张牌由“牌值”字符和“花色”字符组成,注意10用大写字母T表示。牌从小到大的顺序是2,3,4,5,6,7,8,9,T,J,Q,K,A。要求输出比较结果。
例如输入:
2H 3D 5S 9C KD 2C 3H 4S 8C AH (输出:White wins.)
2H 4S 4C 2D 4H 2S 8S AS QS 3S (输出:Black wins.)
2H 3D 5S 9C KD 2C 3H 4S 8C KH (输出:Black wins.)
2H 3D 5S 9C KD 2D 3H 5C 9S KH (输出:Ties.)
----------------------------------------------------------------------------------------
分析:该题目还是属于一道简单题,我们只需要根据题目要求的比较规则去比较即可,我们假设一副牌所属的种类,用level值表示,level越大则牌越大。使用一个函数 int GetLevel(...) 来获取一副牌所属的种类。这里再判断牌的level之前,由于输入是乱序的,为了判别我们需要先把5张牌按从小到大的顺序排序。这里的排序只有5张牌,属于非常小规模的问题,因此我使用了交换数据成本最低的选择排序。同时,两幅牌的level如果相同,则我们还要具体根据牌的点数(value)做进一步判断,因此我们给GetLevel函数再传入一个int数组,把同level的继续比较的指标点数按顺序放入这个数组。这样,当level相同时,我们就继续从头查看order数组里的判别指标来继续判断。该题目的全部代码如下:
ZOJ_1111_POKER_HANDS
/* ZOJ 1111 - Poker Hands
其中选择排序代码引用自:<<C算法(第一卷 基础、数据结构、排序和搜索)(第三版)>>
*/
#include <stdio.h>
#include <string.h>
/*牌的定义*/
typedef struct tagCard
{
char name; /*牌字符:2,3,, A*/
char value; /*牌字符在字符串中的索引*/
char suit; /*花色*/
} Card;
/*-----------排序相关-----------*/
typedef Card Item;
/*两副牌,每幅各有5张牌*/
Card cardsBlack[5];
Card cardsWhite[5];
/*用于在两副牌达到相同level时的辅助比较数组*/
int orderBlack[5];
int orderWhite[5];
/*读入的一行文本!*/
char line[32];
/*所有牌的值,其中10用T表示*/
char CardValues[]="23456789TJQKA";
/*判断t1-t2的差值,即两副牌差值*/
int lessthan(Item t1, Item t2)
{
return (t1.value<t2.value)? 1:0;
}
/*return t1-t2*/
int diffence(Item t1, Item t2)
{
return (t1.value - t2.value);
}
/*交换a[]的i,j项*/
void exchange(Item a[], int i, int j)
{
char n1=a[i].name;
char v1=a[i].value;
char s1=a[i].suit;
a[i].name=a[j].name;
a[i].value=a[j].value;
a[i].suit=a[j].suit;
a[j].name=n1;
a[j].value=v1;
a[j].suit=s1;
}
/*选择排序*/
void SelectionSort(Item a[], int left, int right)
{
int i,j,min;
for(i=left; i<right; i++)
{
min=i;
for(j=i+1; j<=right; j++)
if(lessthan(a[j],a[min])) min=j;
exchange(a, i, min);
}
}
/*---------------------排序相关的代码结束!----------------------*/
/*把读取的这一行输入给两副牌数组Black , White */
/*2H 3D 5S 9C KD 2C 3H 4S 8C AH*/
void InitCards()
{
int i;
for(i=0;i<5;i++)
{
cardsBlack[i].name=line[3*i];
cardsBlack[i].suit=line[3*i+1];
cardsBlack[i].value=strchr(CardValues, cardsBlack[i].name)-CardValues;
cardsWhite[i].name=line[3*i+15];
cardsWhite[i].suit=line[3*i+16];
cardsWhite[i].value=strchr(CardValues, cardsWhite[i].name)-CardValues;
}
}
/*把两副牌按照从小到大的顺序排好序*/
void SortCards()
{
SelectionSort(cardsBlack, 0, 4);
SelectionSort(cardsWhite, 0, 4);
}
/*判断从index起始的,向后数4张牌是否构成一副炸弹,即牌值相同!*/
int IsBomb(Item a[], int i)
{
if( a[i+1].value==a[i].value
&& a[i+2].value==a[i].value
&& a[i+3].value==a[i].value)
return 1;
return 0;
}
/*是否三张相同*/
int IsThree(Item a[], int i)
{
if( a[i+1].value==a[i].value
&& a[i+2].value==a[i].value)
return 1;
return 0;
}
/*是否两张相同的对*/
int IsPair(Item a[], int i)
{
if(a[i+1].value==a[i].value)
return 1;
return 0;
}
/*是否5张牌的牌值连续*/
int IsStraight(Item a[])
{
if( (a[1].value-a[0].value)==1
&& (a[2].value-a[1].value)==1
&& (a[3].value-a[2].value)==1
&& (a[4].value-a[3].value)==1 )
return 1;
return 0;
}
/*是否5张牌都是同花色*/
int IsFlush(Item a[])
{
if( a[1].suit==a[0].suit
&& a[2].suit==a[0].suit
&& a[3].suit==a[0].suit
&& a[4].suit==a[0].suit)
return 1;
return 0;
}
/*得到某副已经排好序的牌等级(同花顺最高!,杂牌最低)*/
/*order是等级相同时的辅助比较值*/
int GetLevel(Item a[], int order[])
{
int level,i,t1,t2,t3;
order[0]=order[1]=order[2]=order[3]=order[4]=0;
/*=============level 9 - Straight flush. 同花顺,花色相同,并且牌连续==================*/
level=9;
if(IsStraight(a) && IsFlush(a))
{
order[0]=a[4].value;/*Ranked by the highest card in the hand. */
return level;
}
/*==============level 8 - Four of a kind. 有4相同值的牌,即一副炸弹 ===================*/
level--;
if(IsBomb(a, 0) || IsBomb(a, 1))
{
/*注意如果有4张相同的牌,则a[1]一定位于这4帐牌之中!!!*/
order[0]=a[1].value;/*Ranked by the value of the 4 cards.*/
return level;
}
/*==============level 7 - Full House. 有3张牌是相同值,另外2张牌也是相同值,OOOXX, XXOOO*/
level--;
if( (IsThree(a,0) && IsPair(a,3)) || (IsThree(a, 2) && IsPair(a, 0)) )
{
/*XXXOO*//*Ranked by the value of the 3 cards. */
order[0]=a[2].value;
return level;
}
/*===============level 6 - Flush. 5张牌同花色================================*/
level--;
if(IsFlush(a))
{
/*If the highest cards have the same value, the hands are ranked by the next highest*/
/*order中是从大到小排列的牌值,用于判别大小*/
for(i=0;i<5;i++) order[i]=a[4-i].value;
return level;
}
/*================level 5 - Straight. 一条龙,即5张牌的值连续.====================*/
level--;
if(IsStraight(a))
{
order[0]=a[4].value;/*ranked by their highest card. */
return level;
}
/*================level 4 - Three of a Kind. 有三张相同值。OOOXY,XOOOY,XYOOO,=====*/
level--;
if(IsThree(a,0) || IsThree(a,1) || IsThree(a,2))
{
order[0]=a[2].value;/*ranked by the value of the 3 cards. */
return level;
}
/*================level 3 - Two Pairs. 两副对,另一个杂牌的索引用i来记录.============*/
level--;
if( (i=0, IsPair(a,1) && IsPair(a,3)) /*-OOII*/
|| (i=2, IsPair(a,0) && IsPair(a,3)) /*OO-II*/
|| (i=4, IsPair(a,0) && IsPair(a,2)) ) /*OOII-*/
{
order[0]=a[3].value;/*I: max pair's value;*/
order[1]=a[1].value;/*O: min pair's value;*/
order[2]=a[i].value;/*-: remaining card; */
return level;
}
/*================level 2 - Pair. 一副对;OOxyz, xOOyz, xyOOz, xyzOO.============*/
/*i存储Pair的位置,t1,t2,t3存储其他值的位置 */
level--;
if( (i=0, t1=4, t2=3, t3=2, IsPair(a,0)) /*OOxyz*/
|| (i=1, t1=4, t2=3, t3=0, IsPair(a,1)) /*xOOyz*/
|| (i=2, t1=4, t2=1, t3=0, IsPair(a,2)) /*xyOOz*/
|| (i=3, t1=2, t2=1, t3=0, IsPair(a,3)) ) /*xyzOO*/
{
/*ranked by the value of the cards forming the pair.
If these values are the same, the hands are ranked
by the values of the cards not forming the pair,
in decreasing order. */
order[0]=a[i].value;
order[1]=a[t1].value;
order[2]=a[t2].value;
order[3]=a[t3].value;
return level;
}
/*=================level 1 - High Card. 完全的杂牌。不构成上诉任何情况,从大到小比=====*/
level--;
for(i=0;i<5;i++) order[i]=a[4-i].value;
return level;
}
int main()
{
int levelBlack, levelWhite, i;
while(gets(line)!=NULL && strlen(line)>2)
{
InitCards(); /*把读入的字符串设置到数组中*/
SortCards(); /*把两副牌都排好序*/
levelBlack=GetLevel(cardsBlack, orderBlack);
levelWhite=GetLevel(cardsWhite, orderWhite);
/*比较两副牌*/
if(levelBlack > levelWhite)
printf("Black wins.\n");
else if(levelBlack < levelWhite)
printf("White wins.\n");
else
{
/*级别相同,需要比较order数组*/
for(i=0;i<5;i++)
{
if(orderBlack[i]>orderWhite[i])
{
printf("Black wins.\n");
break;/*已经比较出结果!*/
}
else if(orderBlack[i]<orderWhite[i])
{
printf("White wins.\n");
break;
}
}
/*依然未分出胜负,则平局!*/
if(i==5) printf("Tie.\n");
}
}
return 0;
}
C代码在ZOJ上的典型内存是160K,因此该代码在AC后顺利跻身解排行榜的第一位。虽然这个题目比较简单,在我看到这个题目的时候就有了AC的把握。代码也很快写好了,然而此后的过程却使得该题成为我AC的最坎坷的题目之一,非常痛苦,在我信心满满确认无误的提交后,却发现每次都是的Segment Fault错误,在无数次调节输入和数组长度后都无法解决该问题,导致我百思不得其解!仔细检查也发现不了任何有野指针或者越界之类的错误。灰心失望之际我把代码拷贝到VC6里面编译,结果VC6的一个警告马上让我发现了问题所在:
原来问题出在我无论如何也不会怀疑的核心判别函数上,即GetLevel函数里的一个判别:
在给t1,t2,t3赋值时,我把t1=.., t2=.., t3=..,粗心的写作了t1=.., t2=.., t1=..; 导致t3是一个不确定值,从而使后续的代码发生了越界!汗!而在TC2.0中却没有给出任何警告! 再改正这个问题以后顺利AC。