初看题目觉得有点恶心,看了书上分析后以自己的理解写了一遍。
思路是回溯法枚举所有情况。
用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;
}