这个东西考试的时候一眼以为状压就压炸了考试又了一下午.....最后我打出来发现后几个点10min都过不去,我大概算了一下,可能是吧.......最后一脸懵逼的我去怂了正解,我们发现只要确定了顺子就可以贪心了,所谓贪心就是在扔相同数码牌的基础上从四带二(对),四带二,三带二,三带一,开始贪心,我们的二,一一定是它本身就是这么大,因为,如果不是的话我们只是浪费了弹药却不能得到优惠,而且由于一个四可以带两个一样的单张,我们就不会出现三拆四捡的情况,我们可以把它们看成四类物品,一类是一个的,一类是两个的......,因此我们发现只有三,四物品才可以去带,而且四可以一下带俩,三可以一下一个,我们就变成了,用两种武器去打两种小怪打得最多为最优。
别忘了剪枝。
#include <cstdio> #include <cstring> #define N 25 #define r register using namespace std; int a[N],c[N],n,T,ans; inline int read() { r int sum=0; r char ch=getchar(); while(ch<'0'||ch>'9')ch=getchar(); while(ch>='0'&&ch<='9') { sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar(); } return sum; } inline int Min(int x,int y) { return x<y?x:y; } inline int Just() { memset(c,0,sizeof(c)); r int t=0; for(r int i=1;i<=14;i++)c[a[i]]++; while(c[4]&&c[2]>=2)c[4]--,c[2]-=2,t++; while(c[4]&&c[1]>=2)c[4]--,c[1]-=2,t++;
while(c[4]&&c[2])c[4]--,c[2]--,t++; while(c[3]&&c[2])c[3]--,c[2]--,t++; while(c[3]&&c[1])c[3]--,c[1]--,t++; return c[1]+c[2]+c[3]+c[4]+t; } void dfs(int now) { if(now>=ans)return; ans=Min(ans,now+Just()); for(r int i=1;i<=11;i++) { r int k=i; while(k<=12&&a[k]>=3)k++; k--; if(k-i+1<2)continue; for(;k-i+1>=2;k--) { for(r int j=i;j<=k;j++)a[j]-=3; dfs(now+1); for(r int j=i;j<=k;j++)a[j]+=3; } } for(r int i=1;i<=10;i++) { r int k=i; while(k<=12&&a[k]>=2)k++; k--; for(;k-i+1>=3;k--) { for(r int j=i;j<=k;j++)a[j]-=2; dfs(now+1); for(r int j=i;j<=k;j++)a[j]+=2; } } for(r int i=1;i<=8;i++) { r int k=i; while(k<=12&&a[k])k++; k--; for(;k-i+1>=5;k--) { for(r int j=i;j<=k;j++)a[j]--; dfs(now+1); for(r int j=i;j<=k;j++)a[j]++; } } } int main() { T=read(),n=read(); while(T--) { memset(a,0,sizeof(a)); for(r int i=1,x;i<=n;i++) { scanf("%d%*d",&x); if(x==1)a[12]++; else if(x==2)a[13]++; else if(x)a[x-2]++; else a[14]++; } ans=Just(); dfs(0); printf("%d\n",ans); } }