HDU 4431 Mahjong (模拟) #by Plato

6 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=4431


有点恶心的模拟。

先贴下第一种方法了,等下补上第二种方法了。。


#include <cstdio>
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
string ans[100];
int suma;
int num[100],tile[20],t[20];
const int sp[20] = {0,1,9,11,19,21,29,31,32,33,34,35,36,37};
bool used[20];

bool Cfu()
{
	for (int i = 1;i <= 40;i++) if (num[i] != 2 && num[i] != 0) return false;
	return true;//竟然忘了这句
}

bool Kfu()
{
	int count2 = 0;
	for (int i = 1;i <= 13;i++)
	{
		int t = sp[i];
		if (num[t] == 0) return false;
		if (num[t] > 1) count2++;
	}
	if (count2 == 1) return true;
	return false;
}

bool DFS(int dep)
{
	if (dep == 5)
		return true;

	for (int i = 1;i <= 14;i++) if (!used[i] && (t[i] != t[i+1] || (t[i] == t[i+1] && used[i+1]))) //(!used[i] && t[i] != t[i+1])
	{
		int tt = t[i];
		if (num[tt] >= 3)
		{
			num[tt] -= 3;
			used[i] = used[i-1] = used[i-2] = 1;
			bool isok = DFS(dep+1);//if (DFS(dep+1)) return true;
			num[tt] += 3;
			used[i] = used[i-1] = used[i-2] = 0;
			if (isok) return true;
		}
		if (tt < 30 && num[tt+1] && num[tt+2])//if (num[tt+1] && num[tt+2])
		{
			num[tt]--,num[tt+1]--,num[tt+2]--;
			int m1,m2;
			for (m1 = i+1;m1 <= 14;m1++) if (!used[m1] && t[m1] == tt+1) break;
			for (m2 = m1+1;m2 <= 14;m2++) if (!used[m2] && t[m2] == tt+2) break;
			used[i] = used[m1] = used[m2] = 1;
			bool isok = DFS(dep+1);//if (DFS(dep+1)) return true;
			num[tt]++,num[tt+1]++,num[tt+2]++;
			used[i] = used[m1] = used[m2] = 0;
			if (isok) return true;
		}
		return false;// add
	}

	return false;
}

bool Fu(int newt)
{
	int ok = false;
	num[newt]++;
	for (int i = 1;i <= 13;i++) t[i] = tile[i];
	t[14] = newt;
	t[0] = t[15] = -1;
	sort(t+1,t+15);

	memset(used,0,sizeof(used));
	for (int i = 1;i <= 14;i++) if (t[i] != t[i-1])
	{
		int tt = t[i];
		if (num[tt] < 2) continue;
		used[i] = used[i+1] = true;
		num[tt] -= 2;
		if (DFS(1)) ok = true;
		used[i] = used[i+1] = false;
		num[tt] += 2;
	}

	if (Cfu()) ok = true;
	if (Kfu()) ok = true;

	num[newt]--;
	return ok;
}

int main()
{
    freopen("test.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while (T--)
    {
        string str;
        memset(num,0,sizeof(num));
        for (int i = 1; i <= 13; i++)
        {
            cin>>str;
            switch (str[1])
            {
            case 'm':
                tile[i] = str[0] - '0';
                break;
            case 's':
                tile[i] = 10 + str[0] - '0';
                break;
            case 'p':
                tile[i] = 20 + str[0] - '0';
                break;
            case 'c':
                tile[i] = 30 + str[0] - '0';
                break;
            }
			num[tile[i]]++;
        }

        suma = 0;
        for (int i = 1;i <= 9;i++)
        	if (num[i] < 4 && Fu(i)) ans[++suma] = char(i+'0'),ans[suma] += "m";
        for (int i = 1;i <= 9;i++)
        	if (num[10+i] < 4 && Fu(10+i)) ans[++suma] = char(i+'0'),ans[suma] += "s";
        for (int i = 1;i <= 9;i++)
        	if (num[20+i] < 4 && Fu(20+i)) ans[++suma] = char(i+'0'),ans[suma] += "p";
        for (int i = 1;i <= 7;i++)
        	if (num[30+i] < 4 && Fu(30+i)) ans[++suma] = char(i+'0'),ans[suma] += "c";

        if (suma > 0) cout<<suma;
        else cout<<"Nooten";
        for (int i = 1;i <= suma;i++) cout<<" "<<ans[i];
        cout<<endl;
    }

    return 0;
}
/*
注意点:
1.只有m,s,p才可以构成顺子了,c是不可以的;
2.每种牌都最多只有4张;
3.题目里的7小对是指7个不同的小对,不能有4个刻子。

检查一般胡牌的情况:1.用DFS,2.

这道题目写得好无语了,先是DFS中TLE了,后来剪枝了下。
然后就是WA,两个原因:1.上面的3个注意点没有考虑到;2.程序的运行逻辑。
*/


/*
附送一组数据(不全面):
9
1s 1s 2s 2s 4s 4s 5s 5s 6s 6s 7s 7s 8s
1s 9s 1m 9m 1p 9p 1c 2c 3c 4c 5c 6c 7c
1s 9s 1m 9m 1p 9p 1c 2c 3c 4c 5c 6c 1m
2s 3s 4s 5s 6s 7s 7s 1c 1c 1c 2c 2c 2c
2s 3s 4s 5s 6s 7s 7s 7s 1c 1c 2c 2c 2c
1s 1s 2s 2s 3s 4s 4s 5s 5s 6s 6s 1c 1c
1s 1s 1s 2s 3s 4s 5s 6s 7s 8s 9s 9s 9s
1s 1s 1s 1s 2s 3s 9s 9s 9s 2s 3s 1c 1c
1s 1s 1s 1s 2s 2s 2s 3s 3s 3s 4s 4s 4s
*/


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值