题目:
https://www.luogu.org/problem/show?pid=2668
思路:
对于顺子的组合,与牌的大小有关,所以要搜;
而对于三带,四带的组合,与大小无关,所以统计一下即可,最后更新答案();
这样减少许多回溯过程,还可以使代码超级短;
总结:
1.注意回溯;
2.看清题面(比如 三顺子至少需要两种牌,不是三种 ,卡我1个小时……);
3.注意细节,最好打完一部分检查一下,加上适当的注释;
4.注意初始化;
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int t,n,cnt[10001],ans,num;
void init()
{
memset(cnt,0,sizeof(cnt));
ans=n;
return;
}
int cnt1,cnt2,cnt3,cnt4,cnt5;
void dfs(int x)
{
cnt1=cnt2=cnt3=cnt4=cnt5=0;
for(int i=1;i<=14;i++)
{
if(cnt[i]==1) cnt1++;
else if(cnt[i]==2) cnt2++;
}
for(int i=1;i<=14;i++)//三带一 三带二
{
if(cnt[i]==3)
{
cnt3++;
if(cnt1>=1) cnt1--;
else if(cnt2>=1) cnt2--;
}
}
for(int i=1;i<=14;i++) //四带二
{
if(cnt[i]==4)
{
cnt4++;
if(cnt1>=2) cnt1-=2;
else if(cnt2>=2) cnt2-=2;
else if(cnt2>=1) cnt2--;//四带一个对子也可以;
}
}
ans=min(ans,x+cnt1+cnt2+cnt3+cnt4);
for(int i=1;i<=8;i++) //单顺子
{
int j;
for(j=i;j<=12;j++)
{
if(cnt[j]<1) break;
cnt[j]--;
if(j-i>=4) dfs(x+1);
}
for(int k=i;k<=j-1;k++) cnt[k]++;
}
for(int i=1;i<=10;i++) // 双顺子
{
int j;
for(j=i;j<=12;j++)
{
if(cnt[j]<2) break;
cnt[j]-=2;
if(j-i>=2) dfs(x+1);
}
for(int k=i;k<=j-1;k++) cnt[k]+=2;
}
for(int i=1;i<=11;i++) // 三顺子
{
int j;
for(j=i;j<=12;j++)
{
if(cnt[j]<3) break;
cnt[j]-=3;
if(j-i>=1) dfs(x+1);
}
for(int k=i;k<=j-1;k++) cnt[k]+=3;
}
return;
}
void solve()
{
init();
int x,y;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(x==0) cnt[14]++;
else if(x==1) cnt[12]++;
else if(x==2) cnt[13]++;
else cnt[x-2]++;
}
dfs(0);
cout<<ans<<endl;
}
int main()
{
scanf("%d%d",&t,&n);
int hh=t;
while(hh--) solve();
return 0;
}