数字麻将(回溯法)

题目描述

数字麻将只包括1~9的数字,每种相同数字的牌最多只能有4张,胡牌的规则是AA + x * (ABC) + y * (AAA),其中AA/AAA表示2张/3张相同数字的牌,ABC表示是三张数字相连的牌,x和y为自然数。听牌是指再增加某一张特定牌后就能胡牌了。如1 1 1 2 3 3 3的听牌为1,2,3或4. 听1时:111+123+33;听2时:111+22+333;听3时:11+123+333;听4时:111+234+33。给定一系列的牌,请编程判断是否听牌。

输入

第一行为整数k,表示有k组测试数据。
每组测试数据占一行,每组测试数据的第一个数为n,表示有n张麻将牌。后面跟有n个整数,表示每张麻将牌数字。

输出

如果没有听牌,则输出“NO!”,否则按递增顺序输出每张听牌。

样例输入

4
4 1 1 3 4
7 1 2 4 5 7 7 8
7 1 1 1 2 3 3 3
10 1 2 3 4 4 5 6 7 8 9

样例输出

2 5
NO!
1 2 3 4
1 4 7

这道题当然第一想法是模拟,但是想来想去总会找到反例,一直不知道如何下手,终于看到老师给的想法后,想到了其实这是回溯法的经典例题,不断尝试各种情况,只要在满足条件下最后牌数为零就行。

注意的地方:一对有且只有一对,一旦确认了一对就够了,顺子和AAA型数量不限,注意回溯的时候恢复原状态就行。

代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int num[10]={0},s[10]={0};
bool flag=0;
int mj(int n)
{
	int i,k;
	if(n==0)return 1;
	for(i=1;i<=9;i++)
		if(num[i]!=0)break;
	if(num[i]>=2&&flag==0)
	{
		num[i]-=2;
		flag=1;
		k=mj(n-2);
		if(k==1)return k;
		num[i]+=2;
		flag=0;
	}
	if(num[i]>=3)
	{
		num[i]-=3;
		k=mj(n-3);
		if(k==1)return k;
		num[i]+=3;
	}
		if(i<=7&&num[i]>=1&&num[i+1]>=1&&num[i+2]>=1)
		{
			num[i]--;
			num[i+1]--;
			num[i+2]--;
			k=mj(n-3);
			if(k==1)return k;
			num[i]++;
			num[i+1]++;
			num[i+2]++;
		}
	return 0;
 } 
 int main()
 {
 	int a,ans,c,k,n;
 	cin>>c;
 	while(c--)
 	{
 		cin>>n;
 		memset(s,0,sizeof(s));
 		for(int j=1;j<=n;j++)
 		{
 			cin>>a;s[a]++;
		} 
		ans=0;
		for(int j=1;j<=9;j++)
		{
			for(k=1;k<=9;k++)
				num[k]=s[k];
			flag=0;
			if(num[j]<4)
				num[j]++;
			k=mj(n+1);
			if(k==1)
			{
				cout<<j<<" ";
				ans++;
			}
		  }
		  if(ans==0)
		  	cout<<"NO!\n";
		  else cout<<endl;  
	 }
 }
坚持!付出终会有回报!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值