链接:https://www.luogu.org/problemnew/show/P2668
码量巨大搜索题(可能是我打的比较菜。。隔壁yzy大佬写的短还跑得快。。),首先想必先搜能一次打多张牌的比较优,因此先暴搜三顺子双顺子单顺子打那些排(注意同一张牌做起点能打多个顺子,这里被卡了2次。。),然后再暴搜四张牌打那些,并且顺便暴搜四张牌是否带排,如果是怎么带,然后三张牌同理,接着在打到两张牌时就可以统计答案了,有两张以上答案加2不然加1,然后就可以了,剪枝的话基本上就只加了一个搜索时步数大于当前答案return。复杂度自然很玄学,但跑的还是蛮快的。。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
int T,n,cnt[30],ans;
void shun1(int,int);void shun2(int,int);void shun3(int,int);void four(int,int);void three(int,int);
void dai2san(int pos,int ps,int foot,int used)
{
if(pos>16)return;
if(cnt[pos]>=1)
{
if(used==1){cnt[pos]--;four(ps+1,foot+1);cnt[pos]++;}
else if(used==0)
{
if(cnt[pos]>=2){cnt[pos]-=2;four(ps+1,foot+1);cnt[pos]+=2;}
cnt[pos]--;dai2san(pos+1,ps,foot,1);cnt[pos]++;
}
}
dai2san(pos+1,ps,foot,used);
}
void dai2dui(int pos,int ps,int foot,int used)
{
if(pos>15)return;
if(cnt[pos]>=2)
{
if(used==1){cnt[pos]-=2;four(ps+1,foot+1);cnt[pos]+=2;}
else if(used==0)
{
if(cnt[pos]>=4){cnt[pos]-=4;four(ps+1,foot+1);cnt[pos]+=4;}
cnt[pos]-=2;dai2dui(pos+1,ps,foot,1);cnt[pos]+=2;
}
}
dai2dui(pos+1,ps,foot,used);
}
void dai1san(int pos,int ps,int foot)
{
if(pos>16)return;
if(cnt[pos]>=1)
{
cnt[pos]--;three(ps+1,foot+1);cnt[pos]++;
}
dai1san(pos+1,ps,foot);
}
void dai1dui(int pos,int ps,int foot)
{
if(pos>15)return;
if(cnt[pos]>=2)
{
cnt[pos]-=2;three(ps+1,foot+1);cnt[pos]+=2;
}
dai1dui(pos+1,ps,foot);
}
void get(int x)
{
int nw=0;
for(int i=3;i<=16;i++)
{
if(cnt[i])
{
if(cnt[i]<=2)nw++;
else nw+=2;
}
}
ans=min(ans,nw+x);
}
void three(int pos,int foot)
{
if(foot>=ans)return;
if(pos>15)
{get(foot);return;}
if(cnt[pos]>=3)
{
cnt[pos]-=3;
three(pos+1,foot+1);
dai1san(3,pos,foot);
dai1dui(3,pos,foot);
cnt[pos]+=3;
}
else three(pos+1,foot);
}
void four(int pos,int foot)
{
if(foot>=ans)return;
if(pos>15)
{three(3,foot);return;}
if(cnt[pos]>=4)
{
cnt[pos]-=4;
four(pos+1,foot+1);
dai2san(3,pos,foot,0);
dai2dui(3,pos,foot,0);
cnt[pos]+=4;
}
else four(pos+1,foot);
}
void shun1(int pos,int foot)
{
if(foot>=ans)return;
if(pos>=11)
{
four(3,foot);
return;
}
int flag=pos-1;
for(int i=pos;i<=14;i++)
{
if(cnt[i]>=1)flag=i;
else break;
}
if(flag-pos>=4)
{
for(int i=pos;i<=flag;i++)cnt[i]--;
for(int i=flag;i>pos+3;i--)
{
shun1(pos,foot+1);
cnt[i]++;
}
cnt[pos]++,cnt[pos+1]++,cnt[pos+2]++,cnt[pos+3]++;
}
shun1(pos+1,foot);
}
void shun2(int pos,int foot)
{
if(foot>=ans)return;
if(pos>=13)
{
shun1(3,foot);
return;
}
int flag=pos-1;
for(int i=pos;i<=14;i++)
{
if(cnt[i]>=2)flag=i;
else break;
}
if(flag-pos>=2)
{
for(int i=pos;i<=flag;i++)cnt[i]-=2;
for(int i=flag;i>pos+1;i--)
{
shun2(pos,foot+1);
cnt[i]+=2;
}
cnt[pos]+=2,cnt[pos+1]+=2;
}
shun2(pos+1,foot);
}
void shun3(int pos,int foot)
{
if(foot>=ans)return;
if(pos>=14)
{
shun2(3,foot);
return;
}
int flag=pos-1;
for(int i=pos;i<=14;i++)
{
if(cnt[i]>=3)flag=i;
else break;
}
if(flag-pos>=1)
{
for(int i=pos;i<=flag;i++)cnt[i]-=3;
for(int i=flag;i>pos;i--)
{
shun3(pos,foot+1);
cnt[i]+=3;
}
cnt[pos]+=3;
}
shun3(pos+1,foot);
}
int main()
{
int hs,num;int cas=0;
//freopen("ot.txt","w",stdout);
scanf("%d%d",&T,&n);
while(T--)
{
// cas++;
ans=1e9;
memset(cnt,0,sizeof cnt);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&num,&hs);
if(num==0)cnt[16]++;
else if(num==1)cnt[14]++;
else if(num==2)cnt[15]++;
else cnt[num]++;
// if(cas==8)cout<<num<<" "<<hs<<endl;
}
// for(int i=3;i<=16;i++)cout<<cnt[i]<<" ";puts("");
shun3(3,0);
printf("%d\n",ans);
}
}