Uva-11210-Chinese Mahjong

初看题目觉得有点恶心,看了书上分析后以自己的理解写了一遍。

思路是回溯法枚举所有情况。

用cnt[i]表示第i张牌有cnt[i]张。

首先将i从0到33枚举听牌,然后j从0到33枚举将牌,相应的cnt数组做修改(但是注意每次改变后到下一种情况前需要将cnt恢复回原来的样子),对use也做修改(use在下面有解释),接着调用dfs(0)。


下面解释dfs(i):

这里的i和书上的不同,书上的i是表示当前组第i个3张牌,所以只要在组第3个3张牌时成功就表示胡了。我的i是指牌的id,即第i张牌。那么,我是怎么判断胡了呢?为此我用变量use表示当前已用手牌数,故当use==14时即可判断牌胡了。


当cnt[i]不等于0时,就要将i牌用完,以i来尝试组成“碰”或者“顺子”,相应更改cnt,use。

注意下一层递归时不是dfs(i+1)而是dfs(i),因为不知道i牌是否用完。


当cnt[i]等于0时,说明i牌没有了,故调用dfs(i+1)。



这题我WA的原因分别是:1.把DONG,NAN,XI等也当做顺子了;

  2.听牌重复输出。

其实我一开始也有注意听牌不能重复输出的,但是以为我的代码已经规避了这个问题,所以没考虑,最后还是用别人的测试数据才找出来的,我想可能是我开始时并不知道什么地方会造成重复输出。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const char* mahjong[] = {"1T", "2T", "3T", "4T", "5T", "6T", "7T", "8T", "9T",
												 "1S", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S",
												 "1W", "2W", "3W", "4W", "5W", "6W", "7W", "8W", "9W",
												 "DONG", "NAN", "XI", "BEI", "ZHONG", "FA", "BAI"			 
}; 

int cnt[35]; //cnt[x] = y 表示第x张牌有y个 
int use; // use表示(通过将,砰,顺子)用掉的牌数,用来判断是否胡了,use == 14表示胡了 
bool ok; // 判断是否存在听牌 

int id(char* msg){
  for(int i = 0; i < 34; i++){
	  if(strcmp(msg,mahjong[i]) == 0) return i;				
  }		
}

//判断是否在“1S ~ 7S” , “1T ~ 7T”, “1W ~ 7W”范围内 
bool chow(int i){
  if(0 <= i && i <= 6) return true;
	if(9 <= i && i <= 15) return true;
	if(18 <= i && i <= 24) return true;
	return false;		 
}
 
bool dfs(int i){  
  if(use == 14) {ok = true; return true;}
  if(i >= 34) return false;  
  if(cnt[i] == 0) return dfs(i+1);
  bool ok2 = false;
  
  if(cnt[i] >= 3){		
	  cnt[i] -= 3;
	  use += 3;
		if(dfs(i))ok2 = true;
		use -= 3;
		cnt[i] += 3;					
  }
  
  if(!ok2 && chow(i) && cnt[i] && cnt[i+1] && cnt[i+2]){
	  cnt[i]--; cnt[i+1]--; cnt[i+2]--;
		use += 3;
		if(dfs(i))ok2 = true;
		use -= 3;
		cnt[i]++; cnt[i+1]++; cnt[i+2]++;	 
  }
  
  return ok2;
}



int main(){		
	char msg[10];
	int kase = 0;
	while(scanf("%s",msg) && strcmp(msg,"0")){
    printf("Case %d: ",++kase);
	  memset(cnt,0,sizeof(cnt));
		ok = false;
		cnt[id(msg)]++;
		for(int i = 0; i < 12; i++){
			scanf("%s",msg);
			cnt[id(msg)]++;		
		}
		
		bool fir = true;
		
		for(int i = 0; i < 34; i++){
		  if(cnt[i] == 4) continue;
			cnt[i]++;
			for(int j = 0; j < 34; j++){
				use = 0;			
				bool ok3 = false;
			  if(cnt[j] >= 2){			
				  cnt[j] -= 2;
				  use += 2;
					if(dfs(0)){									 
					  if(!fir) printf(" ");
						printf("%s",mahjong[i]);
						fir = false; 
						ok3 = true; 					 
				  }
					cnt[j] += 2;					
			  }		
				if(ok3) break; // 防止出现相同输出!!! 
			}
			cnt[i]--;				
	  }
		
		if(ok) printf("\n"); 
		else printf("Not ready\n");											
	}	
  return 0;		
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值