题解代码来自大佬博客https://www.cnblogs.com/Xing-Ling/p/11673395.html
疑问处于dfs里面的注释中
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define Re register int
using namespace std;
int n,x,y,T,ans,gs[20];
inline void in(Re &x){
int f=0;x=0;char c=getchar();
while(c<'0'||c>'9')f|=c=='-',c=getchar();
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
x=f?-x:x;
}
inline void dfs(Re g){
Re p4=0,p3=0,p2=0,p1=0;
for(Re i=1;i<=14;++i){//扫描3~king (1~14)
if(gs[i]==1)++p1;//单牌
if(gs[i]==2)++p2;//双牌
}
for(Re i=1;i<=14;++i)//扫描3~king
if(gs[i]==4){//4牌
++p4;//不管带不带.这个四牌肯定要出
if(p1>=2){p1-=2;continue;}//带两个单牌
if(p2>=2){p2-=2;continue;}//带两个双牌
//这里疑问:带了两个双牌p2-2,为什么单排不会多两张p1+2呢?
if(p2>=1){p2-=1;continue;}//带一个双牌(两个一样的单牌)
//一个四牌(炸弹)
}
for(Re i=1;i<=14;++i)//扫描3~king (1~14)
if(gs[i]==3){//3牌
++p3;//不管带不带,这个三牌肯定要出
if(p1>=1){p1-=1;continue;}//带一个单牌
if(p2>=1){p2-=1;continue;}//带一个双牌
//一个三牌
}
ans=min(ans,g+p1+p2+p3+p4);//没有顺子的最小答案
for(Re i=1,j;i<=8;++i){//单顺子,最大为(10~A)8~12
for(j=i;j<=12;++j){
gs[j]-=1;//反正最后要回溯,先减了再说
if(gs[j]<0)break;//无法继续连下去了,退出
if(j-i+1>=5)dfs(g+1);//单顺子长度至少为5
}
if(j==13)--j;//如果全部连完了,2(13)是不用回溯的
while(j>=i)gs[j]+=1,--j;//最后放在一起回溯
}
for(Re i=1,j;i<=10;++i){//双顺子,最大为Q~A(10~12)
for(j=i;j<=12;++j){
gs[j]-=2;
if(gs[j]<0)break;
if(j-i+1>=3)dfs(g+1);//双顺子长度至少为3
}
if(j==13)--j;
while(j>=i)gs[j]+=2,--j;
}
for(Re i=1,j;i<=11;++i){//三顺子,最大为Q~A(10~12)
for(j=i;j<=12;++j){
gs[j]-=3;
if(gs[j]<0)break;
if(j-i+1>=2)dfs(g+1);//三顺子长度至少为2
}
if(j==13)--j;
while(j>=i)gs[j]+=3,--j;
}
}
int main(){
// freopen("landlords.in","r",stdin);
// freopen("landlords.out","w",stdout);
in(T),in(n);
while(T--){
memset(gs,0,sizeof(gs));
for(Re i=1;i<=n;++i){
in(x),in(y);
if(x==0)++gs[14];//14: 大王
if(x==2)++gs[13];//13: 2
if(x==1)++gs[12];//12: A
if(x>=3)++gs[x-2];//x-2: x
// J: 11-2=9
// Q: 12-2=10
// K: 13-2=11
}
ans=2e9,dfs(0);
printf("%d\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}