HDU 4431 Mahjong (麻将、神坑模拟题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4431


题面:

Mahjong

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 4219    Accepted Submission(s): 842


Problem Description
Japanese Mahjong is a four-player game. The game needs four people to sit around a desk and play with a set of Mahjong tiles. A set of Mahjong tiles contains four copies of the tiles described next:

One to nine Man, which we use 1m to 9m to represent;

One to nine Sou, which we use 1s to 9s to represent;

One to nine Pin, which we use 1p to 9p to represent;

Character tiles, which are:Ton, Nan, Sei, Pei, Haku, Hatsu, Chun, which we use 1c to 7c to represent.

A winning state means a set of 14 tiles that normally contains a pair of same tiles (which we call "eyes") and four melds. A meld is formed by either three same tiles(1m, 1m, 1m or 2c, 2c, 2c for example) or three continuous non-character tiles(1m, 2m, 3m or 5s, 6s, 7s for example).

However, there are two special winning states that are different with the description above, which are:
"Chii Toitsu", which means 7 different pairs of tiles;
"Kokushi Muso", which means a set of tiles that contains all these tiles: 1m, 9m, 1p, 9p, 1s, 9s and all 7 character tiles. And the rest tile should also be one of the 13 tiles above.

And the game starts with four players receiving 13 tiles. In each round every player must draw one tile from the deck one by one. If he reaches a winning state with these 14 tiles, he can say "Tsu Mo" and win the game. Otherwise he should discard one of his 14 tiles. And if the tile he throws out can form a winning state with the 13 tiles of any other player, the player can say "Ron" and win the game.

Now the question is, given the 13 tiles you have, does there exist any tiles that can form a winning state with your tiles?

(Notes: Some of the pictures and descriptions above come from Wikipedia.)
 

Input
The input data begins with a integer T(1≤T≤20000). Next are T cases, each of which contains 13 tiles. The description of every tile is as above.
 

Output
For each cases, if there actually exists some tiles that can form a winning state with the 13 tiles given, print the number first and then print all those tiles in order as the description order of tiles above. Otherwise print a line "Nooten"(without quotation marks).
 

Sample Input
   
   
   
   
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
 

Sample Output
   
   
   
   
2 1p 4p Nooten
 

Source
2012 Asia Tianjin Regional Contest
 
解题:

    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.逻辑需要推敲,不能想当然。


你可能感兴趣的:(模拟,HDU,区域赛)