Luogu2668 斗地主/Luogu2540 斗地主增强版
https://www.luogu.com.cn/problem/P2668
https://www.luogu.com.cn/problem/P2540
搜索
我们搜索顺子,最优选择应该是先搜单顺或者双顺,再搜三顺(毕竟三张牌用处更大)
搜顺子可以从长的搜到短的
最优性剪枝就不用多说了吧
然后每搜到一个状态,对散牌进行处理
首先枚举把四张牌、三张牌拆开的情况(用了六重循环)
由于我们枚举了拆开的情况,剩下的牌都是整份整份打出的(三张牌就必须三张牌一起打出,不能拆成\(1+2,1+1+1\))
然后先用四带二,把单牌和对牌尽量拿走
比较讨厌的是小王和大王,只有其中一张与单牌无异,两张的话,如果还有四张牌,那么让四张牌把讨厌的小王和大王一块儿吃了,如果没有的话,到三张牌的时候再处理
如果有多余的一张牌、两张牌,特判即可
接下来剩余的四张牌都是炸弹
好了,然后是三带一、三带二和剩余的散牌
令\(S_i\)表示含有\(i\)张的牌的种类数量,\(a_0\)表示小王、大王的数量
\[ans=ans+
\begin{cases}
S_3 & (S_1+S_2+a_0 \le S_3)\\
S_1+S_2+a_0 & (S_1+S_2+a_0 > S_3 且 a_0<2)\\
S_1+S_2+a_0 & (S_1+S_2+a_0 = S_3+1 且 a_0=2)\\
S_1+S_2+a_0-1 & (S_1+S_2+a_0 > S_3+1 且 a_0=2)\\
\end{cases}
\]
第三种情况一开始没判,导致计算出来的数值反而比标准答案小,因为小王、大王其中一张被三带一了,所以火箭没用
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int T,n,x,y,ans,a[14],rk[5],k[5];
int check()
{
int g=0;
//四带二
if (k[4]<=(k[1] >> 1))
{
g+=k[4];
k[1]-=k[4] << 1;
k[4]=0;
} else
{
g+=k[1] >> 1;
k[4]-=k[1] >> 1;
k[1]&=1;
}
if (k[4]<=(k[2] >> 1))
{
g+=k[4];
k[2]-=k[4] << 1;
k[4]=0;
} else
{
g+=k[2] >> 1;
k[4]-=k[2] >> 1;
k[2]&=1;
}
int a0=a[0];
if (a0 && k[4])
{
if (a0==1)
{
if (k[1])
g++,k[4]--,k[1]--; else
k[1]++;
} else
g++,k[4]--;
a0=0;
}
if (k[4])
{
if (k[2])
{
k[2]=0;
k[4]--;
g++;
}
}
g+=k[4];
//三带二、三带一、单牌、对牌
if (k[3]>=k[1]+k[2]+a0)
g+=k[3]; else
g+=k[1]+k[2]+a0-(int)(a0==2 && k[1]+k[2]>=k[3]);
return g;
}
int ck()
{
int g=105;
memset(rk,0,sizeof(rk));
for (int i=1;i<=13;i++)
rk[a[i]]++;
for (int i=0;i<=rk[4];i++)
for (int i2=0;i2<=rk[4]-i;i2++)
for (int i3=0;i3<=rk[4]-i-i2;i3++)
for (int i4=0;i4<=rk[4]-i-i2-i3;i4++)
for (int j=0;j<=rk[3];j++)
for (int j2=0;j2<=rk[3]-j;j2++)
{
memcpy(k,rk,sizeof(rk));
k[4]-=i,k[1]+=i,k[3]+=i;//4=1+3
k[4]-=i2,k[2]+=i2 << 1;//4=2+2
k[4]-=i3,k[1]+=i3 << 2;//4=1+1+1+1
k[4]-=i4,k[1]+=i4 << 1,k[2]+=i4;//4=1+1+2
k[3]-=j,k[1]+=j,k[2]+=j;//3=1+2
k[3]-=j2,k[1]+=j2*3;//3=1+1+1
g=min(g,check());
}
return g;
}
void dfs(int cs)
{
if (cs>=ans)
return;
int o=ck();
ans=min(ans,cs+o);
if (!o)
return;
//双顺
int s[14];
s[13]=(int)(a[13]>=2);
for (int i=12;i>=2;i--)
if (a[i]<2)
s[i]=0; else
s[i]=s[i+1]+1;
for (int i=2;i<=11;i++)
{
for (int j=s[i];j>=3;j--)
{
for (int k=i;k<i+j;k++)
a[k]-=2;
dfs(cs+1);
for (int k=i;k<i+j;k++)
a[k]+=2;
}
}
//单顺
s[13]=(int)(a[13]>=1);
for (int i=12;i>=2;i--)
if (!a[i])
s[i]=0; else
s[i]=s[i+1]+1;
for (int i=2;i<=9;i++)
{
for (int j=s[i];j>=5;j--)
{
for (int k=i;k<i+j;k++)
a[k]--;
dfs(cs+1);
for (int k=i;k<i+j;k++)
a[k]++;
}
}
//三顺
s[13]=(int)(a[13]>=3);
for (int i=12;i>=2;i--)
if (a[i]<3)
s[i]=0; else
s[i]=s[i+1]+1;
for (int i=2;i<=12;i++)
{
for (int j=s[i];j>=2;j--)
{
for (int k=i;k<i+j;k++)
a[k]-=3;
dfs(cs+1);
for (int k=i;k<i+j;k++)
a[k]+=3;
}
}
}
int main()
{
scanf("%d%d",&T,&n);
while (T --> 0)
{
memset(a,0,sizeof(a));
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if (x==1)
x=13; else
if (x)
x--;
a[x]++;
}
ans=ck();
dfs(0);
printf("%d\n",ans);
}
return 0;
}