题解:本题主要考查搜索+模拟
简要题意:打斗地主,但斗地主的规则略有不同,没有飞机只有三顺子。求最少出完牌的次数。
1.搜索+模拟:首先本题解只是针对于NOIP原题数据,没有考虑拆牌组牌情况。若想要强力的数据,请移步增强版。
本题策略就是搜顺子,因为能快速的出牌。主要就是要模拟和细心。
坑点:
1.四带一和四带二不一定是最优的,所以还要考虑四张拆为三带一或二!!!(被坑了好久,看题解才恍然大悟)
2.大小王是不能当作对子三带二和四带二的!!
3.二和大小王是不能当顺子。
代码如下:
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
int a[666666];
int n,t,ans;
void dfs(int p)
{
if(p>=ans)return ;
int q=0;
for(int i=3;i<=14;i++)// 顺子
{
if(a[i]<=2)q=0;
else
{
q++;
if(q>=2)
{
for(int j=i;j>=i-q+1;j--)a[j]-=3;
dfs(p+1);
for(int j=i;j>=i-q+1;j--)a[j]+=3;
}
}
}
q=0;
for(int i=3;i<=14;i++)
{
if(a[i]<=1)q=0;
else
{
q++;
if(q>=3)
{
for(int j=i;j>=i-q+1;j--)a[j]-=2;
dfs(p+1);
for(int j=i;j>=i-q+1;j--)a[j]+=2;
}
}
}
q=0;
for(int i=3;i<=14;i++)
{
if(a[i]==0)q=0;
else
{
q++;
if(q>=5)
{
for(int j=i;j>=i-q+1;j--)a[j]--;
dfs(p+1);
for(int j=i;j>=i-q+1;j--)a[j]++;
}
}
}
for(int i=2;i<=14;i++)
{
if(a[i]==4)//四带
{
a[i]-=3;
for(int j=2;j<=15;j++)
{
if(a[j]==0||i==j)continue;
a[j]--;
dfs(p+1);
a[j]++;
}
for(int j=2;j<=14;j++)
{
if(a[j]<=1||i==j)continue;
a[j]-=2;
dfs(p+1);
a[j]+=2;
}
a[i]+=3;
a[i]-=4;
for(int j=2;j<=15;j++)
{
if(a[j]==0||i==j)continue;
a[j]--;
for(int z=2;z<=15;z++)
{
if(a[z]==0||j==z)continue;
a[z]--;
dfs(p+1);
a[z]++;
}
a[j]++;
}
for(int j=2;j<=14;j++)
{
if(a[j]<=1||i==j)continue;
a[j]-=2;
for(int z=2;z<=14;z++)
{
if(a[z]<=1||j==z)continue;
a[z]-=2;
dfs(p+1);
a[z]+=2;
}
a[j]+=2;
}
a[i]+=4;
}
if(a[i]==3)//三带
{
a[i]-=3;
for(int j=2;j<=15;j++)
{
if(a[j]==0||i==j)continue;
a[j]--;
dfs(p+1);
a[j]++;
}
for(int j=2;j<=14;j++)
{
if(a[j]<=1||i==j)continue;
a[j]-=2;
dfs(p+1);
a[j]+=2;
}
a[i]+=3;
}
}
for(int i=2;i<=15;i++)
if(a[i]!=0)p++;
ans=min(ans,p);
}
int main()
{
cin>>t>>n;
while(t--)
{
int x,y;ans=0x3f3f3f3f;
memset(a,0,sizeof(a));
for(int i=1;i<=n;i++)
{
cin>>x>>y;
if(x==0)a[15]++;
if(x==1)a[14]++;
else a[x]++;
}
dfs(0);
cout<<ans<<endl;
}
return 0;
}