题目描述
数字麻将只包括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个整数,表示每张麻将牌数字。
每组测试数据占一行,每组测试数据的第一个数为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;
}
}
坚持!付出终会有回报!!