【分析】
这题真是令人手动再见
玩了这么多年斗地主现在才知道王还能被三带一带走。
其实题目给的还算清楚,都是我惯性思维惹的祸qwqqq。
这道题要用 搜索+贪心+小小最优性剪枝
搜索搜三种情况:顺子,连对,三张连对
然后贪心:优先四带连对,然后四带二(注意四带二两种情况,在代码里有体现:带的两张有可能是相同牌),然后三带二,三带一,最后是单排,对子,三张,炸弹,直接模拟统计计入答案。
下了一堆测试数据才过,代码中间三段其实可以压缩,因为本质相同,但是当时脑抽,人又懒qwqqq。
【代码】
//NOIP 2015 斗地主
#include<iostream>
#include<cstdio>
#include<cstring>
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int inf=1000000;
int n,m,T,ans;
int b[16],c[5];
inline int czy()
{
int i,j,k,tot=0;
memset(c,0,sizeof c);
fo(i,0,14) c[b[i]]++;
while(c[4] && c[1]>1) c[4]--,c[1]-=2,tot++;
while(c[4] && c[2]>1) c[4]--,c[2]-=2,tot++;
while(c[4] && c[2]) c[4]--,c[2]--,tot++;
while(c[3] && c[1]) c[3]--,c[1]--,tot++;
while(c[3] && c[2]) c[3]--,c[2]--,tot++;
return c[1]+c[2]+c[3]+c[4]+tot;
}
inline void dfs(int stp,int yu)
{
if(stp==ans) return;
if(yu==0) {ans=stp;return;}
bool flag=0;
int s,e,i,j,k,tot=0;
fo(i,3,10)
{
fo(j,i,i+4)
if(!b[j]) break;
if(j==i+5)
{
s=i;
fo(j,i,15)
{
if(!b[j])
{
flag=1;
e=j-1;
break;
}
}
}
else i=j;
if(flag)
{
flag=0;
tot++;
int l;
fo(l,4,e-s)
{
fo(i,s,e-l)
{
fo(j,i,i+l)
b[j]--;
dfs(stp+1,yu-l-1);
fo(j,i,i+l)
b[j]++;
}
}
i=e;
}
}
flag=0;
fo(i,3,12) //连对
{
fo(j,i,i+2)
if(b[j]<2) break;
if(j==i+3)
{
s=i;
fo(j,i,15)
{
if(b[j]<2)
{
e=j-1;
flag=1;
break;
}
}
}
else i=j;
if(flag)
{
flag=0;
tot++;
int l;
fo(l,2,e-s)
{
fo(i,s,e-l)
{
fo(j,i,i+l)
b[j]-=2;
dfs(stp+1,yu-2*(l+1));
fo(j,i,i+l)
b[j]+=2;
}
}
i=e;
}
}
flag=0;
fo(i,3,13) //三顺子
{
fo(j,i,i+1)
if(b[j]<3) break;
if(j==i+2)
{
s=i;
fo(j,i,15)
{
if(b[j]<3)
{
e=j-1;
flag=1;
break;
}
}
}
else i=j;
if(flag)
{
flag=0;
tot++;
int l;
fo(l,1,e-s)
{
fo(i,s,e-l)
{
fo(j,i,i+l)
b[j]-=3;
dfs(stp+1,yu-3*(l+1));
fo(j,i,i+l)
b[j]+=3;
}
}
i=e;
}
}
ans=min(ans,czy()+stp);
}
int main()
{
int i,j,k,x;
scanf("%d%d",&T,&n);
while(T--)
{
int f=0;
int tmp;
ans=inf;
memset(b,0,sizeof b);
fo(i,1,n)
{
scanf("%d",&tmp);
if(tmp==1) tmp=14;
scanf("%d",&x);
b[tmp]++;
}
dfs(0,n);
printf("%d\n",ans);
// if(k==65) return 0;
}
return 0;
}