这个代码在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"); } }