Chinese Mahjong--Uva11210中国麻将--暴搜

题目链接https://vjudge.net/contest/305270#problem/I
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


题目大意:在麻将游戏中,有 34 种牌,分别是 1-9 的万,1-9 的筒,1-9 的条,以及 7 种字牌 “东”,“南”,“西”,“北”,“白”,“发”,“中”。

这 34 种牌各四张,共 136 张。

如果你手牌中凑够了 14张牌,满足一定的牌型条件,就称作“和牌”。

这个牌型有很多种,但在本题中,只考虑最基本的一种,4 个面子 + 1 个雀头组成的牌型。

面子是刻子和顺子的统称,雀头是对子。

刻子指的是,三张一样的牌。

顺子指的是,三张连续的牌,注意,只有非字牌(也就是万,筒,条)才能连续,例如一二三万,四五六筒,并且不能循环连续,比如九一二条就不算顺子。

对子指的是,两张一样的牌。

现在,你 手中有13 张牌,并且再得到某一张牌,就可以和牌,而你的目标就是输出能使 你和牌的牌

emmm,和HDU4331其实是一样的,暴力DFS回溯,枚举34种牌,判断是否可以和牌:我们用mjid保存每张牌的数量,mj保存的是牌的编号。
先打个表:

string sple[]={"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"};
for (int i=0; i<34; i++) {
	memset(mjid,0,sizeof(mjid));
	for (int j=0; j<13; j++)  mjid[mj[j]]++;
	if (mjid[i]>=4) continue;
	mjid[i]++;
	if (ok(i)) {
		mark=1;
		cout<<" "<<sple[i];
	}
	mjid[i]--;
}
if (!mark) printf (" Not ready");
cout<<endl;

下面关键的就是解决ok函数了。我们可以枚举对子的情况然后判断剩下的是否可以组成4个面子就好了,这个就需要用到dfs回溯了,我们可以将每张牌进行选择,即组成刻子或是顺子:

int check(int stp)
{
	for (int i=0; i<34; i++){//组成刻子(3张相同的牌)
		if (mjid[i]>=3){
			if (stp==3) return 1;
			mjid[i]-=3;
			if (check(stp+1)) return 1;
			mjid[i]+=3;
		}
	}
	for (int i=0; i<25; i++){//组成顺子,前面数字为8、9的和字牌都是无用的
		if (i%9>6) continue;//判断前面的数字是否为8、9
		if (mjid[i] && mjid[i+1] && mjid[i+2]){
			if (stp==3) return 1;
			mjid[i]--;mjid[i+1]--;mjid[i+2]--;
			if (check(stp+1)) return 1;
			mjid[i]++;mjid[i+1]++;mjid[i+2]++;
		}
	}
	return 0;
}

emmm,其实这两个for可以合并的。。。
以下是AC代码:

#include <bits/stdc++.h>
using namespace std;
string sple[]={"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"};
string s;
int mjid[40],mj[15];
int findid(string s);
int ok(int id);
int check(int stp);
int main()
{
	int num=0,cas=1;
	while (cin>>s){
		if (s[0]=='0') break;
		mj[num++]=findid(s);
		if (num==13){
			num=0;
			int mark=0;
			printf ("Case %d:",cas++);
			for (int i=0; i<34; i++){
				memset(mjid,0,sizeof(mjid));
				for (int j=0; j<13; j++)  mjid[mj[j]]++;
				if (mjid[i]>=4) continue;
				mjid[i]++;
				if (ok(i)){
					mark=1;
					cout<<" "<<sple[i];
				}
				mjid[i]--;
			}
			if (!mark) printf (" Not ready");
			cout<<endl;
		}
	}
	return 0;
}
int findid(string s)
{
	for (int i=0; i<34; i++)
		if (s==sple[i]) return i;
}
int ok(int id)
{
	for (int i=0; i<34; i++){
		if (mjid[i]>=2){
			mjid[i]-=2;
			if (check(0)) return 1;
			mjid[i]+=2;
		}
	}
	return 0;
}
int check(int stp)
{
	for (int i=0; i<34; i++){
		if (mjid[i]>=3){
			if (stp==3) return 1;
			mjid[i]-=3;
			if (check(stp+1)) return 1;
			mjid[i]+=3;
		}
	}
	for (int i=0; i<25; i++){
		if (i%9>6) continue;
		if (mjid[i] && mjid[i+1] && mjid[i+2]){
			if (stp==3) return 1;
			mjid[i]--;mjid[i+1]--;mjid[i+2]--;
			if (check(stp+1)) return 1;
			mjid[i]++;mjid[i+1]++;mjid[i+2]++;
		}
	}
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值