[NOIP2015] 斗地主
这是个很繁琐的贪心+模拟题。但是好在NOIP是随机数据,所以数据强度不是很大qwq
做题的时候我们注意一些细节就可以了。
-
并不是纯模拟+搜索,需要贪心。注意顺序:优先考虑顺子,之后是带牌,最后才是散牌
-
四带二可以带散牌,也可以带两个对子
-
有的情况二不能带
-
有一个优化是最后炸弹,对子,单牌,火箭,三张牌的处理同意按照出牌次数+1处理,因为如果可以出的话前面搜索就搜过了,现在剩下肯定是只能自己出自己的。
然后就没有了,代码如下:(里面的multiset是调试用的qwq)
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#define MAXN 20
using namespace std;
int t,ans,n;
int cnt[MAXN];
multiset<string>v;
inline void print()
{
printf("ans=%d\n",ans);
for(multiset<string>::iterator it=v.begin();it!=v.end();it++)
cout<<*it<<" ";
cout<<endl<<endl;
return;
}
inline void search(int sum)
{
if(sum>=ans) return;
for(int i=3;i<=13;i++)//判断三顺子
{
if(cnt[i]>=3&&cnt[i+1]>=3)
{
int pos=i+2;
while(cnt[pos]>=3&&pos<=14)pos++; pos--;
for(int k=i+1;k<=pos;k++)
{
for(int j=i;j<=k;j++)
cnt[j]-=3;
v.insert("三顺子"),search(sum+1),v.erase("三顺子");
for(int j=i;j<=k;j++)
cnt[j]+=3;
}
}
}
for(int i=3;i<=12;i++)//判断双顺子
{
if(cnt[i]>=2&&cnt[i+1]>=2&&cnt[i+2]>=2)
{
int pos=i+3;
while(cnt[pos]>=2&&pos<=14)pos++; pos--;
for(int k=i+2;k<=pos;k++)
{
for(int j=i;j<=k;j++)
cnt[j]-=2;
v.insert("双顺子"),search(sum+1),v.erase("双顺子");
for(int j=i;j<=k;j++)
cnt[j]+=2;
}
}
}
for(int i=3;i<=10;i++)//判断单顺子
{
if(cnt[i]>=1&&cnt[i+1]>=1&&cnt[i+2]>=1&&cnt[i+3]>=1&&cnt[i+4]>=1)
{
int pos=i+5;
while(cnt[pos]&&pos<=14)pos++; pos--;
for(int k=i+4;k<=pos;k++)
{
for(int j=i;j<=k;j++)
cnt[j]--;
v.insert("单顺子"),search(sum+1),v.erase("单顺子");
for(int j=i;j<=k;j++)
cnt[j]++;
}
}
}
for(int i=3;i<=15;i++)//四带二
{
if(cnt[i]>=4)
{
for(int j=3;j<=15;j++)//带两个对子
{
if(i==j||cnt[j]<2) continue;
for(int k=3;k<=15;k++)
{
if(i==k||j==k||cnt[k]<2) continue;
cnt[i]-=4,cnt[j]-=2,cnt[k]-=2;
v.insert("4-2对子"),search(sum+1),v.erase("4-2对子");
cnt[i]+=4,cnt[j]+=2,cnt[k]+=2;
}
}
for(int j=3;j<=15;j++)//带两张散牌
{
if(i==j||cnt[j]==0) continue;
for(int k=3;k<=15;k++)
{
if(i==k||j==k||cnt[k]==0) continue;
cnt[i]-=4,cnt[j]--,cnt[k]--;
v.insert("4-2散牌"),search(sum+1),v.erase("4-2散牌");
cnt[i]+=4,cnt[j]++,cnt[k]++;
}
}
}
}
for(int i=3;i<=15;i++)
{
if(cnt[i]>=3)
{
for(int j=3;j<=15;j++)//3-对子
{
if(cnt[j]<2||i==j) continue;
cnt[i]-=3,cnt[j]-=2;
v.insert("3-2"),search(sum+1),v.insert("3-2");
cnt[i]+=3,cnt[j]+=2;
}
for(int j=3;j<=16;j++)//3-1
{
if(cnt[j]==0||j==i) continue;
cnt[i]-=3,cnt[j]--;
v.insert("3-1"),search(sum+1),v.erase("3-1");
cnt[i]+=3,cnt[j]++;
}
}
}
for(int i=3;i<=16;i++) if(cnt[i]) sum++;
ans=min(ans,sum);
//print();
}
int main()
{
scanf("%d%d",&t,&n);
while(t--)
{
memset(cnt,0,sizeof(cnt));
ans=0x3f3f3f3f;
for(int i=1;i<=n;i++)
{
int id,color;
scanf("%d%d",&id,&color);
if(id==1) cnt[14]++;
else if(id==2) cnt[15]++;
else if(id==0) cnt[16]++;
else cnt[id]++;
}
//for(int i=3;i<=15;i++)
// printf("cnt[%d]=%d\n",i,cnt[i]);
search(0);
printf("%d\n",ans);
}
return 0;
}