hdu 4431 Mahjong,uva 11210 chinese Mahjong,麻将,超快的解法。。

这个代码在hdu可以进前五,运行时间是140ms(我贴的代码是156ms,不用cin cout 就可以140ms了好像)

题目就是我们熟悉的麻将,题目会给你13 张牌,你判他能不能胡,以及胡那些牌

ok题意很简单是吧,开始,各种暴力吧,以及各种剪枝,。。。有些剪枝原理我没写注释相信聪明的你可以明白的。。

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
int card[40],ans[40],cardtemp[40],numcard[4];
int smalldealint[10];
inline int deal();
void read();
inline int is7()//判7对的函数
{
    for(int i=0;i<36;i++)
    if(card[i]&&card[i]!=2) return 0;
    return 1;
}
inline int is131()//判13幺的函数
{
    if((card[0]&&card[9]&&card[8]&&card[17]&&card[18]&&card[26]&&card[27]&&card[28]&&card[29]&&card[30]&&card[31]&&card[32]&&card[33])
    &&(card[0]+card[9]+card[8]+card[17]+card[18]+card[26]+card[27]+card[28]+card[29]+card[30]+card[31]+card[32]+card[33]==14))
    return 1;
    return 0;
}
inline int smalldeal(int p,int n);
inline int getnum(int p,int n)//数同种花色的牌有几种
{
    int num=0;
    for(int i=p;i<n+p;i++)
        num+=card[i];
    return num;
}
inline void output(int numans,int ans[]);
int main()
{
    int T;cin>>T;
    while(T--)
    {
        memset(ans,0,sizeof(ans));
        memset(cardtemp,0,sizeof(cardtemp));
        int numans=0;
        read();
        memset(numcard,0,sizeof(numcard));
        for(int i=0;i<28;i+=9)
            numcard[i/9]=getnum(i,9);
        copy(card,card+39,cardtemp);
        for(int i=0;i<34;i++)//枚举哪张牌可以听
            if(cardtemp[i]<4)
            {
                copy(cardtemp,cardtemp+35,card);//把card备份一下,因为下面的操作要修改card数组
                if(card[i+1]==0&&i!=0&&card[i-1]==0&&(card[i]==0||card[i]==3)) continue;//各种剪枝
                //不懂得想一下就懂了
                card[i]++;//加入这张牌
                numcard[i/9]++;//把同种花色的牌的个数加1
                if(is7()||is131()) numans+=ans[i]=1;//如果是十三幺或7对
                else if(deal()) numans+=ans[i]=1;//不然判普通的胡法
                memset(card,0,sizeof(card));//把牌清零,貌似是废话
                numcard[i/9]--;//把同种花色的牌的个数减回没有加牌之前的状态
            }
        output(numans,ans);//输出结果
    }
    return 0;
}
inline int deal()//普通的判胡的处理函数
{
    int num2=0;
    for(int i=0;i<4;i++)
        if(numcard[i]%3==1) return 0;//有单张牌
        else if (numcard[i]%3==2) num2++;//判一下不同花色的牌有几个花色的个数是3*k+2的个数
    if(num2!=1) return 0;//如果同种花色的牌的个数是3*k+2的形式的没有或不止一种 ,则不可能胡牌
    for(int i=0;i<3;i++)//先判 饼 索 万牌的形式能不能胡
    {
        if(numcard[i]%3==0)//如果同种花色牌的个数是3的倍数,则里面没有眼
        {
            if(!smalldeal(i*9,9)) return 0;//3的倍数的牌不合法,则整副牌也不合法
        }
        else//说明牌的个数是3K+2里面有眼,现在需要枚举这对眼
        {
            int f=0;
            copy(&card[i*9],&card[i*9+9],smalldealint);//因为smalldeal函数会直接修改数组,所以要备份一下
            for(int j=i*9;j<i*9+9;j++)
            {
                copy(smalldealint,smalldealint+9,&card[i*9]);//备份还原
                 if(card[j]>1)
                {
                    card[j]-=2;//枚举眼
                    if(smalldeal(i*9,9)) {f=1;break;}//眼合法就不用枚举了
                }
            }
            if(!f) return 0;//枚举眼都不合法,这幅牌也不合法了
        }
    }
    //处理 风牌,东南西北什么的
    if(numcard[3]%3==2)//眼在风牌里面
    for(int i=27;i<34;i++)
    {
        if(card[i]==4) return 0;//四张风直接返回假值
        if(card[i]==2) {card[i]=0;break;}//这里可能也有一个眼,找到一个眼就不用找了
    }
    for(int i=27;i<35;i++)//那么剩下的风牌必须全是3张或0张
    if(card[i]&&card[i]!=3) return 0;
    return 1;
}
inline int smalldeal(int p,int n)//处理3的倍数的牌是不是全能组成3个3个合法牌
{//返回真假值
    for(int i=p;i<p+n;i++)
        if(card[i])
        {
            if(card[i]>=3) card[i]=card[i]-3;
            if(card[i])
            {
                if(!card[i+1]||!card[i+2]||i+2>=p+n) return 0;
                if(card[i]>card[i+1]||card[i]>card[i+2]) return 0;
                card[i+1]-=card[i],card[i+2]-=card[i];
                card[i]=0;
            }
        }
    return 1;
}
inline void read()//读入函数
{
    memset(card,0,sizeof(card));
    for(int i=0;i<13;i++)
    {
        getchar();
        char a[5];
        scanf("%s",a);
        int num=(int)(a[0]-'0');
        if(a[1]=='m') card[num-1]++;
        if(a[1]=='s') card[num-1+9]++;
        if(a[1]=='p') card[num-1+18]++;
        if(a[1]=='c') card[num-1+27]++;
    }
}
inline void output(int numans,int ans[])//输出结果的函数
{
    if(!numans) printf("Nooten\n");//如果没有牌可以听
    else
    {
        cout<<numans;
        for(int i=0;i<35;i++)//按大小输出结果
        if(ans[i])
        {
            if(i/9==0) printf(" %dm",i%9+1);
            else if(i/9==1) printf(" %ds",i%9+1);
            else if(i/9==2) printf(" %dp",i%9+1);
            else printf(" %dc",i%9+1);
        }
       printf("\n");
    }
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值