回 帖 发 新 帖 刷新版面

主题:第33次编程比赛第2题

胡了!
=====

先抄一段话:来自http://sports.enorth.com.cn/system/2006/04/26/001290452.shtml
[quote]
麻将起源于中国,原属皇家和王宫贵族的游戏,其历史可以追溯到三四千年前。在长期的历史演变过程中,麻将逐步从宫廷流传到民间,至清朝中期基本定型。麻将作为中国传统文化宝库中的一个重要组成部分,具有集益智性、趣味性、博弈性于一体的运动魅力,及内涵丰富、底蕴悠久的东方文化特征。几百年来,麻将曾经风行于大江南北,流行范围涉及到社会的各个阶层,已经进入千家万户,而且流传海外,成为我国国内及国外华人中最具规模和影响力的智力体育活动。 
[/quote]

先大致介绍一下麻将规则:
各地的麻将有各自的特色,规则上有许多差异。
麻将牌有条子、筒子、万子,分别从1到9各4张。
一条、二条、...九条
一筒、二筒、...九筒
一万、二万、...九万
"东南西北中发白"字牌各4张
有的麻将还有花牌"春夏秋冬梅兰竹菊"各1张
共计144张牌。

玩家有一手牌3n+1张(n为0,1,2...)。根据13张麻将和16张麻将的玩法n的上限不同,分别为4和5。一手牌再加上别家打出的一张牌或者自己摸进的一张牌,组成3n+2张牌可以判断是否胡牌。

基本牌型分为刻、顺、杠、将。
刻是3张同样的牌。
顺是3张同一花色连号的条子、筒子或万子。
将是2张同样的牌。
杠是4张同样的牌(由于杠牌时会补一张牌,所以玩家手牌数量仍然是3n+1张)

吃牌、碰牌、杠牌
吃牌是取上家打出的牌和自己的牌组成顺。
碰牌是取别家打出的牌和自己的牌组成刻。
杠牌是取别家打出的牌或者自己摸到的牌和自己的牌组成杠。
吃碰的玩家不摸牌,但仍然需要打出一张。
杠牌的玩家需要补一张牌,并仍然需要打出一张。

麻将一般4人围成一桌,分为东南西北四家,轮流做庄。游戏开始各自初始拿到一样数目的牌(13张麻将拿13张,16张麻将拿16张)。从上局赢家开始摸牌,摸一张牌打一张牌。只有下家可以吃上家打出的牌,任何玩家都可以碰(或者杠)其他玩家打出的牌。

在游戏中,玩家需要想办法使得牌型成为某一种胡牌的类型。
要胡牌,一般需要配成一将搭配若干个刻或者顺。(也有其他特殊类型的胡牌,不同类型胡牌有翻(或者台)的累积。翻数越高,赢得越多)

百搭麻将是一种有趣的变种。许多麻将玩法中都有百搭牌的玩法。游戏开始时一般通过骰子确定某张牌是百搭牌。百搭牌可以根据需要替代任何牌参与组合。但百搭牌不允许被打出。

现在需要大家写一个函数判断玩家否满足胡牌条件。
为了简化判断,牌只出现条子、筒子、万子和字牌。并且牌数保证是3n+2张(手牌+1张牌),其中0<=n<=5。预先吃、碰、杠的牌均不需要考虑。胡牌只判断3n+2张牌是否满足一将搭配若干个刻或者顺。不需要计算翻(或者台数)。当有3张或4张百搭牌时,也直接算胡。

并且规定:
条子是 0x01到0x09
筒子是 0x11到0x19
万子是 0x21到0x29
东南西北分别是0x30,0x40,0x50,0x60
中发白分别是0x70,0x80,0x90
百搭牌是0x00

当牌A被选择为百搭牌时,白板(0x90)就转为代替A牌。可以参与刻、顺、将的组合。注意白板本身也可能会被选中为百搭牌。调用时,白板已经被转为百搭牌本身的牌,所以除非白板本身是百搭,否则参数中不会出现0x90。

bool TestHu(const char pai[], int count, char baida);
pai数组内保存了count个牌(已经按编码由小到大排序)
TestHu函数需要返回[color=red]true[/color]表示牌型可以胡牌。否则返回[color=red]false[/color]
baida是被选中的百搭牌。

下面举几个例子:
0x00, 0x01, 0x02, 0x03, 0x30, baida = 0x40,胡牌,顺+将,百搭配将
0x00, 0x11, baida = 0x11,胡牌,将,百搭归位(它配为它本身的牌,注意这里的0x10其实本来是白板)
0x01, 0x02, 0x03, 0x04, 0x05, baida = 0x50, 不胡,无百搭
0x01, 0x02, 0x03, 0x04, 0x04, baida = 0x50, 胡,无百搭,顺+将
0x00, 0x00, 0x01, 0x01, 0x01, 0x02, 0x02, 0x03,baida = 0x50, 胡,刻+顺+将(其实此时有许多种搭配都可以)
0x00, 0x00, 0x00, 0x00, 0x01, 0x12, 0x23, 0x60, baida = 0x50, 胡,4百搭

百搭规则参考温州麻将,我家这边百搭规则和它不一样,不过这个规则相对我家这边的百搭规则容易一些
最后扯一下,麻将游戏消遣可以,赌博就不好了。

回复列表 (共7个回复)

沙发

/*大家都这么客气啊~~
 我就不客气啊~先交(虽然不敢保证结果)
*/
bool TestHu(const char pai[], int count,char baida){
    int jiang,erqueyi,danyi;
    int i=-1;
    int flag;
    int low;
    jiang=erqueyi=danyi=0;
    while(1){
        if(pai[++i]!=0x00){
            flag=i;
            break;
        }

    };
    if(flag>=3)
        return true;
    for(low=flag;low<count;){
        if(low+2<count){
            if(pai[low]==pai[low+1]&&pai[low+1]==pai[low+2]||
                pai[low+1]-pai[low]==1&&pai[low+2]-pai[low+1]==1){
                low+=3;
            }
            else if(pai[low]==pai[low+1]&&pai[low+1]!=pai[low+2]){
                jiang++;
                low+=2;
            }
            else if( pai[low+1]-pai[low]==1&&pai[low+2]-pai[low+1]!=1||
                pai[low+1]-pai[low]==2&&low+3<count&&
                !(pai[low+3]-pai[low+2]==1&&pai[low+2]-pai[low+1]==1)||
                low+3<count&&!(pai[low+1]==pai[low+2]&&pai[low+2]==pai[low+3]) ){
                low+=2;
                erqueyi++;
            }
            else{
                danyi++;
                low++;
            }
        }
        
        else if(low+1<count){
            if(pai[low+1]-pai[low]==1){
                erqueyi++;
                break;
            }
            else if(pai[low+1]==pai[low])
                jiang++;
            else
                danyi+=2;
            low+=2;
        }
        else{
            danyi++;
            low++;
        }
    }
    if(jiang!=0)
        return ( (jiang-1)*1+2*danyi+erqueyi==flag );
    else
        return ( (danyi-1)*2+erqueyi==flag-1 );
}
    

板凳

这么久都没人交??
折腾出个了,好幸运第一遍测试就通过了给出的数据,不知道还有没有bug
先占个位了~~嘿嘿

bool deal(char *p,int num,int j,int i){//j记录有无将,i记录白搭个数
    if(num==0)
        return true;
    if(*p==0x00&&deal(p+1,num,j,i+1))
        return true;
    if(*(p+1)==*p&&deal(p+2,num-2,1,i))//配将
        return true;
    else if(i>=1&&j!=1&&deal(p+1,num-2,1,i-1))//用白搭配将
        return true;
    if(num>=3){
        if((*(p+2)-*(p+1))==(*(p+1)-*p)&&deal(p+3,num-3,j,i))//配刻或顺
            return true;
        else if(i>=1&&(*(p+1)-*p)<=2&&deal(p+2,num-3,j,i-1))//用一个白搭配刻或顺
            return true;
        else if(i>=2&&deal(p+1,num-3,j,i-2))//用两个白搭配刻或顺
            return true;
    }
    return false;
}

bool TestHu(const char pai[], int count, char baida){
    char pai_tmp[17]={0};
    for(int i=0;i<count;i++){
        if(pai[i]==0x90&&baida!=0x00)
            pai_tmp[i]=baida;
        else
            pai_tmp[i]=pai[i];
        }
    if(deal(pai_tmp,count,0,0))
        return true;
    return false;
}

3 楼

/*
    3n+2(0=<n<=5)张麻将牌,3、4张百搭胡。
    麻将牌表示:
    条:0x01~0x09
    筒:0x11~0x19
    万:0x21~0x29
    东南西北中发白:0x30、0x40、0x50、0x60、0x70、0x80、0x90
    百搭:0x00
    当牌A 被选中为百搭,白板(0x90)就转为牌A

    参数:
    pai:由小到大排序的麻将牌
    count:牌数
    baida:被选为百搭的牌
    返回值:true可胡  false不可胡
*/
#include <ctime>
#include <cstdlib>
#include <iostream>
using namespace std;

struct CTiao{
    int pai[10];//0表示这组牌的百搭牌个数 1-9表示1-9条的个数
    int min,max;//最小/大的条
    int sum;//条的个数
    int summod;//sum mod 3
    CTiao(){
        for(int i = 0;i<10;i++)
            pai[i] = 0;
        min = max = sum = summod = 0;
    }
};

bool hu(const CTiao cmj)//无百搭
{
    CTiao mj = cmj;
    int i;
    if(mj.sum == 0)
        return true;
    for(i=mj.min;i<8;i++){
        if(mj.pai[i] == 1 || mj.pai[i] == 2){
            mj.pai[0] -= 3 * mj.pai[i];
            if((mj.pai[i+2] -= mj.pai[i])<0)
                return false;
            if((mj.pai[i+1] -= mj.pai[i])<0)
                return false;
            mj.pai[i] = 0;
            return hu(mj);
        }
        else if(mj.pai[i]>=3){
            mj.pai[i] -= 3;
            mj.pai[0] -= 3;
            return hu(mj);
        }
    }
    if(mj.pai[8] % 3 == 0 && mj.pai[9] % 3 == 0)
        return true;
    else
        return false;
}

//穷举
bool hu_baida_all(const CTiao cmj)
{
    CTiao mj = cmj;
    int i,j;

    if(mj.pai[0] >= mj.sum)
        return true;
    
    mj.min = (mj.min - mj.pai[0])>0 ? mj.min - mj.pai[0] : 1;
    mj.max = (mj.max + mj.pai[0])<10 ? mj.max + mj.pai[0] : 9;
    CTiao tmp;
    if(mj.pai[0] == 1){                
        for(i=mj.min;i<=mj.max;i++){
            tmp = mj;
            tmp.sum += mj.pai[0];
            tmp.summod = tmp.sum % 3;
            tmp.pai[i] ++;
            tmp.pai[0] = 0;
            if(hu(tmp))
                return true;
        }
        return false;
    }
    else if(mj.pai[0] == 2){
        for(i=mj.min;i<=mj.max;i++)
            for(j=mj.min;j<=mj.max;j++){
                tmp = mj;
                tmp.sum += mj.pai[0];
                tmp.summod = tmp.sum % 3;
                tmp.pai[i] ++;
                tmp.pai[j] ++;
                tmp.pai[0] = 0;
                if(hu(tmp))
                    return true;
            }
        return false;
    }    
}


4 楼

发部上了
发到你的油箱里面 zikaizhang@gmail.com 
楼主帮我贴出来吧

郁闷

5 楼

tested under Gcc 3.4.3

bool solve(const char pai[], int count,int pos,int baidanum)
{
    int type;
    char backup[20],*curp;

    curp = const_cast<char *>(pai);
    if( count>0 && pos<count )
    {
        int startpos,kk;

        memcpy(backup,&curp[pos],count-pos);
        for(kk=pos;kk<count && (unsigned char)curp[kk] > (unsigned char)0x90 ; ++kk);
        startpos = kk;
        if( startpos==count && (baidanum>1 || 0==baidanum) ) return(true);
        for(type=0;type<3;++type) /* 0 shun, 1 jiang, 2 ke */
        {
            char outloop=0;

            for(int i=startpos; i<count && !outloop; ++i)
            {
                if( (unsigned char)curp[i] > (unsigned char)0x90 ) continue;
                switch(type)
                {
                    case 0: /* shun */
                    {
                        int getv;
                        if( (getv=curp[i]/10)>=0 && getv<3 )
                        {
                            int k=1,getpos[2];
                            for(int j=i+1; j<count && k<3; ++j)
                                if( curp[i]+k ==curp[j] ) getpos[k-1] = j, ++k;
                            if( 3==k )
                            {
                                curp[i] = curp[getpos[0]] = curp[getpos[1]] = (unsigned char)0x91;
                                if( solve(pai,count,i+1,baidanum) ) return(true);
                                memcpy(&curp[pos], backup, count-pos);
                            }
                            if( k+baidanum>2 )
                            {
                                curp[i] = (unsigned char)0x91;
                                if( baidanum>1 && solve(pai,count,i+1,baidanum-2) ) return(true);
                                memcpy(&curp[pos], backup, count-pos);
                                if( k>1 )
                                {
                                    curp[i] = curp[getpos[0]] = (unsigned char)0x91;
                                    if( baidanum>0 && solve(pai,count,i+1,baidanum-1) ) return(true);
                                    memcpy(&curp[pos], backup, count-pos);
                                }
                            }
                        }
                        outloop = 1;
                        break;
                    }
                    case 1: /* jiang */
                    case 2: /* ke */
                    {
                        int k=0,getpos[2];

                        for(int j=i+1; j<count && k<2; ++j)
                            if( curp[i]==curp[j] ) getpos[k] = j, ++k;
                        if( 2==k )
                        {
                            curp[i] = curp[getpos[0]] = curp[getpos[1]] = (unsigned char)0x91;
                            if( solve(pai,count,i+1,baidanum) ) return(true);
                            memcpy(&curp[pos], backup, count-pos);
                        }
                        if( 1==k )
                        {
                            curp[i] = curp[getpos[0]] = (unsigned char)0x91;
                            if( solve(pai,count,i+1,baidanum) ) return(true);
                            memcpy(&curp[pos], backup, count-pos);
                        }
                        if( baidanum>0 )
                        {
                            curp[i] = (unsigned char)0x91;
                            if( solve(pai,count,i+1,baidanum-1) ) return(true);
                            memcpy(&curp[pos], backup, count-pos);
                            if( k>0 )
                            {
                                curp[i] = curp[getpos[0]] = (unsigned char)0x91;
                                if( solve(pai,count,i+1,baidanum-1) ) return(true);
                                memcpy(&curp[pos], backup, count-pos);
                            }
                            if( baidanum>1 )
                            {
                                curp[i] = (unsigned char)0x91;
                                if( solve(pai,count,i+1,baidanum-2) ) return(true);
                                memcpy(&curp[pos], backup, count-pos);
                            }
                        }

                        outloop = 1;
                        break;
                    }
                }
            }
        }
    }
    else return( baidanum>1 || 0==baidanum );

    return(false);
}

bool TestHu(const char pai[], int count, char baida)
{
    int pos=0,baidanum;
    char *curp;

    curp = const_cast<char *>(pai);
    for(int i=0;i< count; ++i)
        if( !(pai[i]==0 || ((unsigned char)baida==(unsigned char)0x90 && pai[i]==baida)) ) curp[pos++] = pai[i];
    baidanum = count - pos;
    return( solve(pai,pos,0,baidanum) );
}

6 楼

int NumberOfBaiTa(const char pai[], int count); /*百搭牌的数量*/
int Jiang(const char pai[], int count, int n); /*是否成将(可以补牌),返回牌的位置  */
int Sun(const char pai[], int count, int n);  /*是否成顺(可以补牌),返回牌的位置  */
int Ke(const char pai[], int count, int n);   /*是否成刻(可以补牌),返回牌的位置  */

int (*Pattern[])(const char*,int,int) = {Jiang, Sun, Ke}; /*转移表*/
int nbaita, njiang;/*百塔牌数和将牌数*/

bool SlovePai(const char pai[], int count,int n);
bool TestHu(const char pai[], int count, char baida); 

bool TestHu(const char pai[], int count, char baida)
{
    int n ;

    njiang = 0;
    nbaita = n = NumberOfBaiTa(pai,count);
    if(nbaita > 2) return true;
    return SlovePai(pai,count,n);
}
bool SlovePai(const char pai[], int count, int n)
{
    int i,t,tb,tj;
        
    if(n == count)
    { 
        if(nbaita == 0 && njiang == 1)
              return true;
        else
            return false;
    }
    for(i = 0; i < 3; i++)/*轮换使用,将(一次),顺,刻*/
    {
        tb = nbaita; tj = njiang;
        t = Pattern[i](pai,count,n);
        if(t != 0 && SlovePai(pai,count,n+t))
            return true;
        nbaita = tb;njiang = tj;
    }    
    return false;
}
int Jiang(const char pai[], int count, int n)
{
    if( (n+1) < count && pai[n] == pai[n+1])
    {
        njiang += 1;
        return 2;
    }
    if(nbaita > 0)/*使用一张百塔*/
    {
        nbaita -= 1;
        njiang += 1;
        return 1;
    }
    if(nbaita > 1)/*使用两张*/
    {
        nbaita -= 2;
        njiang +=1;
        return 0;
    }
    return 0;
}
int Sun(const char pai[], int count, int n)
{
    if( (n+2) < count &&(pai[n+2]-pai[n+1]) == 1 && (pai[n+1]-pai[n]) == 1)
    {
        return 3;
    }
    if( (n+1) < count && (pai[n+1]-pai[n]) <= 2 && nbaita > 0)
    {
         nbaita -= 1;/*使用一张*/
         return 2;
    }
    if(nbaita > 1)/*使用两张*/
    {
        nbaita -= 2;
        return 1;
    }
    return 0;
}
int Ke(const char pai[], int count, int n)
{
     if( (n+2) < count && pai[n] == pai[n+1] && pai[n] == pai[n+2])
    {
         return 3;
      }
      if( (n+1) < count && pai[n] == pai[n+1] && nbaita > 0)
      {
           nbaita -= 1;/*使用一张*/
           return 2;
    }
    if(nbaita > 1)/*使用两张*/
    {
        nbaita -= 2;
        return 1;
    }
    return 0;
}
int NumberOfBaiTa(const char pai[], int count)
{
    int n = 0;
    while(n < count && pai[n] == 0) ++n;
    return n;
}

7 楼

int NumberOfBaiTa(const char pai[], int count); /*百搭牌的数量*/
/*是否成将,刻,顺(可以补牌),返回牌的位置  */
int Jiang(const char pai[], int count, int n); 
int Ke(const char pai[], int count, int n);   
int Sun(const char pai[], int count, int n);  

int (*Pattern[])(const char*,int,int) = {Jiang,Ke,Sun}; /*转移表*/
int nbaita, njiang;/*百塔牌数和将牌数*/

bool SlovePai(const char pai[], int count,int n);
bool TestHu(const char pai[], int count, char baida); 

bool TestHu(const char pai[], int count, char baida)
{
    int n ;
    njiang = 0;
    nbaita = n = NumberOfBaiTa(pai,count);
    if(nbaita > 2) return true;
    return SlovePai(pai,count,n);
}
bool SlovePai(const char pai[], int count, int n)
{
    int i,t,tb,tj;
        
    if(n == count)
    { 
        if(nbaita == 0 && njiang == 1)
              return true;
        else
            return false;
    }
    for(i = 0; i < 3; i++)/*轮换使用,将(一次)刻,顺,*/
    {
        tb = nbaita; tj = njiang;
        t = Pattern[i](pai,count,n);
        if(t != 0 && SlovePai(pai,count,n+t))
            return true;
        nbaita = tb;njiang = tj;
    }    
    return false;
}
int Jiang(const char pai[], int count, int n)
{
    if( (n+1) < count && pai[n] == pai[n+1])
    {
        njiang += 1;
        return 2;
    }
    if(nbaita > 0)/*使用一张百塔*/
    {
        nbaita -= 1;
        njiang += 1;
        return 1;
    }
    if(nbaita > 1)/*使用两张*/
    {
        nbaita -= 2;
        njiang +=1;
        return 0;
    }
    return 0;
}
int Ke(const char pai[], int count, int n)
{
     if( (n+2) < count && pai[n] == pai[n+1] && pai[n] == pai[n+2])
    {
         return 3;
      }
      if( (n+1) < count && pai[n] == pai[n+1] && nbaita > 0)
      {
           nbaita -= 1;/*使用一张*/
           return 2;
    }
    if(nbaita > 1)/*使用两张*/
    {
        nbaita -= 2;
        return 1;
    }
    return 0;
}
int Sun(const char pai[], int count, int n)
{
    if( (n+2) < count &&(pai[n+2]-pai[n+1]) == 1 && (pai[n+1]-pai[n]) == 1)
    {
        return 3;
    }
    if( (n+1) < count && (pai[n+1]-pai[n]) <= 2 && nbaita > 0)
    {
         nbaita -= 1;/*使用一张*/
         return 2;
    }
    return 0;
}

int NumberOfBaiTa(const char pai[], int count)
{
    int n = 0;
    while(n < count && pai[n] == 0) ++n;
    return n;
}

我来回复

您尚未登录,请登录后再回复。点此登录或注册