题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4431
题面:
2 1s 2s 3s 2c 2c 2c 2p 3p 5m 6m 7m 1p 1p 1p 1p 2p 3p 4s 5s 6s 7c 7c 3s 3s 2m 2m
2 1p 4p Nooten
wa了八次,只能说自己太水或者说太懒,都不自己编几组样例测试下。题意是比较复杂的,做法是枚举加34张牌,看是否可以胡。可以赢的情况是3种。
1.有1筒,9筒,1万,9万,1索,9索,东南西北中发白。其中一种为2张。(国士无双)
2.有7对不同的对子。
3.4对顺子或者小包子+1对将军
顺子是指1s,2s,3s,包子是指1p,1p,1p。(以上专业术语摘自别人博客)
坑点:
1.注意是不同的对子。
2.检查时若把数量变更了,记得还原。
3.枚举34张牌的时候,注意牌数不能超过4张,此处神坑,但也说明自己思维不够全面,没有考虑到保证状态的合法性。
代码:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; int cnt[35],cal[35],res[35],tmp[35]; //判断当前组合是否能够通过方式三胡 bool check() { int sign=0; for(int i=1;i<=34;i++) tmp[i]=cal[i]; //此处是能在当前组合就组合,否则在后面就无法成顺子了 for(int i=1;i<=9;i++) { while(tmp[i]) { if(tmp[i]>=3) { tmp[i]-=3; sign++; } else if(i<=7&&tmp[i+1]&&tmp[i+2]) { tmp[i]--; tmp[i+1]--; tmp[i+2]--; sign++; } else break; } } for(int i=10;i<=18;i++) { while(tmp[i]) { if(tmp[i]>=3) { tmp[i]-=3; sign++; } else if(i<=16&&tmp[i+1]&&tmp[i+2]) { tmp[i]--; tmp[i+1]--; tmp[i+2]--; sign++; } else break; } } for(int i=19;i<=27;i++) { while(tmp[i]) { if(tmp[i]>=3) { tmp[i]-=3; sign++; } else if(i<=25&&tmp[i+1]&&tmp[i+2]) { tmp[i]--; tmp[i+1]--; tmp[i+2]--; sign++; } else break; } } for(int i=28;i<=34;i++) { if(tmp[i]==3) { tmp[i]=0; sign++; } } //4对方合法 if(sign==4)return true; else return false; } int main() { int t,n,ans; char c; scanf("%d",&t); while(t--) { //编号m:1-9 s:10-18 p:19-27 c:28-34 ans=0; memset(cnt,0,sizeof(cnt)); memset(res,0,sizeof(res)); for(int i=0;i<13;i++) { scanf("%d%c",&n,&c); if(c=='m') cnt[n]++; else if(c=='s') cnt[n+9]++; else if(c=='p') cnt[n+18]++; else cnt[n+27]++; } //拷贝 for(int i=1;i<=34;i++) { for(int j=1;j<=34;j++) cal[j]=cnt[j]; cal[i]++; //某张牌数大于4,不合法 if(cal[i]>4)continue; //通过方式一胡 bool flag=true; if(cal[1]&&cal[9]&&cal[10]&&cal[18]&&cal[19]&&cal[27]) { cal[1]=cal[9]=cal[10]=cal[18]=cal[19]=cal[27]=0; for(int j=28;j<=34;j++) { if(cal[j]==0) flag=false; else cal[j]=0; } if(flag) { for(int k=1;k<=34;k++) { if(cal[k]) { flag=false; break; } } if(flag) { ans++; res[i]=1; continue; } } } for(int j=1;j<=34;j++) cal[j]=cnt[j]; cal[i]++; int x=0; //通过方式二胡 for(int j=1;j<=34;j++) { if(cal[j]==2) x++; } if(x==7) { ans++; res[i]=1; continue; } //枚举方式三 for(int j=1;j<=34;j++) { if(cal[j]>=2) { cal[j]-=2; if(check()) { ans++; res[i]=1; cal[j]+=2; break; } cal[j]+=2; } } } if(ans) { printf("%d",ans); for(int i=1;i<=9;i++) { if(res[i]) printf(" %dm",i); } for(int i=10;i<=18;i++) { if(res[i]) printf(" %ds",i-9); } for(int i=19;i<=27;i++) { if(res[i]) printf(" %dp",i-18); } for(int i=28;i<=34;i++) { if(res[i]) printf(" %dc",i-27); } printf("\n"); } else printf("Nooten\n"); } return 0; }
总结:
1.一些变量使用后,记得还原。代码长了,确实难发现,最好能精简代码。
2.自己生成的状态要确保合法,若不是看了别人的博客,怕是很难发现这个问题。
3.代码确定无误后,要从数据合法性出发去考虑。
4.还是那句话,不是样例过了,就行了,不如花上5分钟,把代码讲一遍给小鸭子听。(除非是特别短的代码,自己非常有把握的)
5.不要总是想着对的那个方向,要小心意外情况的发生。
6.逻辑需要推敲,不能想当然。