ZOJ 1111 - Poker Hands 解题报告

          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。

时间: 2024-09-10 09:18:23

ZOJ 1111 - Poker Hands 解题报告的相关文章

ZOJ - 1098 Simple Computer 解题报告

Simple Computers Time Limit: 1 Second      Memory Limit: 32768 KB You are to write an interpreter for a simple computer. This computer uses a processor with a small number of machine instructions. Furthermore, it is equipped with 32 byte of memory, o

ZOJ 1205 - Martian Addition 解题报告

          http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1205           题目说明:(把题目从GOOGLE翻译的结果修改而来)           在22世纪,科学家们发现智能居民生活在火星.火星人非常喜欢数学.每一年,他们将举行一次火星算术大赛(计算机) ,竞赛内容是计算两个100位数的和,使用时间最少的人获得冠军.今年,他们还邀请地球上的人参加竞赛.            作为唯一代表地球,你发

ZOJ 1635 - Directory Listing 解题报告

       题目:1635 Directory Listing(列出目录)        http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=635         看描述好像是chenyue姐姐出的题目.这道题的大意是,给出一个UNIX文件系统的树,以及树节点的自身size,按要求列出它们,保持适当的缩进,并统计每个节点的总的size.输入的第一行代表根结点,每一个节点由name size或者*name size的形式组成(如

杭电ACM 2000-&amp;gt;2099 100道题 详细解题报告出炉

我去年暑假花了5天,把杭电ACM网站上2000到2099这100道题全AC了,又花了10来天精心写解题报告.里面包括题目.解题思路.编程技巧以及参考源码.所有代码都是使用C/C++写的. 最近整理资料时无意间发现,打包成chm文件和大家分享.我已经上传到CSDN上了.下载地址:http://download.csdn.net/source/492194 也可到我的Google Sites上下载. 题号 题名 题号 题名 2000 ASCII码排序 2001 计算两点间的距离 2002 计算球体积

ZOJ 3505. Yet Another Set of Numbers 解题报告

ZOJ 3505:Yet Another Set of Numbers 地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3505   题意:有一个数字集合,集合中的数遵循以下规则:   ( 1 ). 每个数字的最高位不是 0 ; ( 2 ). 每个数字包含最多 N 位,且只有 0,1,2,3 这四个数字可能出现(0 < N < 20); ( 3 ). 每个数字的相邻位不同(例如:301是有效的,300不是); (

ZOJ 2529 - A+B in Hogwarts 解题报告

          题目2529:A+B in Hogwarts           链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1535           题目描述:哈利波特去的魔法学院使用一种特殊进制法表示数字:第i位用第i个素数为进制(radix),例如"个位"的进制为第一个素数2,"十位"的进制为第二个素数3,"百位"的进制为第三个素数5,...依此类推.例

ZOJ 1010. Area 解题报告

ZOJ 1010. Area. 题目地址:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1010 基本题意:此题目给出 n 个点的坐标 (xi,yi ) ,要求求出这些点按顺序围成的多边形的面积.同时要求检测出不合法的多边形(例如非相邻边彼此相交的情况).     此题求面积不难,样例很容易就过了,但是提交 10+ 次基本都是 WA ,几乎失去信心.做题最郁闷的就是你不知道问题到底出在什么地方,可能改了很多次都没有试对地

ZOJ 1090 - The Circumference of the Circle 解题报告

      题目的链接在这里:       http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1090       题目描述很简单,大意是,给出三个点的坐标,设为A(x1,y1),B (x2, y2),C (x3, y3),然后求出通过这三点的圆的周长(保留两位小数).但推导公式却比较麻烦,我是这样来做的.       首先根据同一个弦的圆心角角度相同,不难得出,圆周的直径d= BC/ sin a = AC/ sin b =

ZOJ 1009, 1115, 1476, 1733, 2405 解题报告

        星期天这天一口气AC了五道题,除了1009外基本都可算是简单题.        (1)1009 Enigma:        http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1009        题目是讲解二战期间德国使用的密码机Enigma.明文通过按键,通过转盘(rotor)转换为密文.如下图所示为有1个转盘,一共有6个字母的情况,每击键一次,转盘转动一格.如果含有多个转盘,则以类似数字进制方式转动,