【分析】
强烈建议做一下luoguP2540斗地主增强版,人不能只满足于随机数据,因为出题人往往都会出一些神奇数据来满足你被卡的愿望。
这里讲一下我看见的某位大神的思路:先用f[i][j][k][l]表示当有i个炸弹,有j个三,有k个对子,有l个单牌时最少的出牌数(不考虑顺子),用动归求出每个数值,留作后用。(其实就是预处理,逃)。然后dfs顺子(单顺子、双顺子、三顺子),分别求出出顺子次数与剩下不出顺子的和值,再取其中最小值。
两张鬼牌可以看作两张单牌进入f数组,或者作为单独的一张对牌打出(不能被当成一对带)
下面放出经过我稍加修改的代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[30][30][30][30],ha[5];
int c[20],shun[4]={0,5,3,2};
int T,n,ans,king;
int calc(int a,int b,int c,int d)
{
if(king==1) return f[d][c][b][a+1];
else if(king==2) return min(f[d][c][b][a]+1,f[d][c][b][a+2]);
else return f[d][c][b][a];
}
void dfs(int step)
{
if(step>ans) return;
memset(ha,0,sizeof(ha));
for(int i=2;i<=14;i++)
ha[c[i]]++;
ans=min(ans,step+calc(ha[1],ha[2],ha[3],ha[4]));
for(int k=1;k<=3;k++)
{
for(int i=3;i<=14;i++)
{
int j;
for(j=i;j<=14&&c[j]>=k;j++)
{
c[j]-=k;
if(j-i+1>=shun[k]) dfs(step+1);
}
for(j--;j>=i;j--)
c[j]+=k;
}
}
}
int main()
{
freopen("landlords.in","r",stdin);
freopen("landlords.out","w",stdout);
cin>>T>>n;
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
for(int l=0;l<=n;l++)
{
if(i*4+j*3+k*2+l>n) continue;
f[i][j][k][l]=i+j+k+l;
if(i)
{
if(i>=1)//4->3+1
f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j+1][k][l+1]);
if(k>=2)//四带二(对子)
f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k-2][l]+1);
if(i>=2)//两炸拆成四带二
f[i][j][k][l]=min(f[i][j][k][l],f[i-2][j][k][l]+1);
if(k>=1)
f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k-1][l]+1);
if(l>=2)//四带二(单牌)
f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k][l-2]+1);
f[i][j][k][l]=min(f[i][j][k][l],f[i-1][j][k][l]+1);
}
if(j)
{
if(j>=1)//3->2+1
f[i][j][k][l]=min(f[i][j][k][l],f[i][j-1][k+1][l+1]);
if(k>=1)//
f[i][j][k][l]=min(f[i][j][k][l],f[i][j-1][k-1][l]+1);
if(l>=1)
f[i][j][k][l]=min(f[i][j][k][l],f[i][j-1][k][l-1]+1);
f[i][j][k][l]=min(f[i][j][k][l],f[i][j-1][k][l]+1);
}
if(k)
f[i][j][k][l]=min(f[i][j][k][l],f[i][j][k-1][l]+1);
if(l)
f[i][j][k][l]=min(f[i][j][k][l],f[i][j][k][l-1]+1);
}
while(T--)
{
memset(c,0,sizeof(c));
king=0;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(x==0) king++;
else if(x==1) c[14]++;
else c[x]++;
}
ans=0;
for(int i=2;i<=14;i++)
if(c[i]) ans++;
if(king) ans++;
dfs(0);
printf("%d\n",ans);
}
return 0;
}