[LOJ2422]【NOIP2015】斗地主
大名鼎鼎的NOIP2015D1T3
题意:
由于一些众所周知的原因,没有完整题面……
给你一副斗地主的手牌(牌数<=23),问最少要几次能出完;
包含双王,没有癞子,连对要三连对以上,可以直接出三张同点数的牌(三对),可以出连着的三对,也可以三带一,三带二,但不能出飞机,可以四张带一对或两张单牌。
题解:
神题啊……orz……虽然写起来并不麻烦,但是当年这个神之题面真是雷倒了无数选手……
提前来做的原因是今天xfz出了一道毒瘤题(密码:yxqak)……是这题的弱化版(其实是道水题啦),本蒟蒻看到题面就吓傻了不敢打,改完题来膜一下原题……
首先注意到花色和点数大小是没用的(王也可以被带着出),且出牌时除非出顺子(连对,连三对),否则出牌的顺序是不会影响到最后答案的;
并且牌数很少(毕竟是斗地主嘛……),最多只会有四个顺子,所以可以直接暴力dfs把所有顺子找出来,枚举怎么出每一个顺子,然后再贪心处理当前的出牌情况;
很容易想到把牌组合起来出越多越好,贪心优先四带二,三带二,三带一,剩余的炸弹,三对,对子和单张直接出掉;
于是就……做完了。
ps:然而实际上由于本题数据太小,爆搜可以轻松通过
(听说欢乐斗地主上100w豆即可一秒切掉此题)
代码:
1 #include<algorithm>
2 #include<iostream>
3 #include<cstring>
4 #include<cstdio>
5 #include<cmath>
6 #include<queue>
7 #define inf 2147483647
8 #define eps 1e-9
9 using namespace std;
10 typedef long long ll;
11 int t,n,x,y,ans,num[17],tot[5];
12 int gao(){
13 int ret=0;
14 memset(tot,0,sizeof(tot));
15 for(int i=3;i<=16;i++){
16 tot[num[i]]++;
17 }
18 while(tot[4]&&tot[2]>=2){
19 tot[4]--;
20 tot[2]-=2;
21 ret++;
22 }
23 while(tot[4]&&tot[1]>=2){
24 tot[4]--;
25 tot[1]-=2;
26 ret++;
27 }
28 while(tot[4]&&tot[2]){
29 tot[4]--;
30 tot[2]--;
31 }
32 while(tot[3]&&tot[2]){
33 tot[3]--;
34 tot[2]--;
35 ret++;
36 }
37 while(tot[3]&&tot[1]){
38 tot[3]--;
39 tot[1]--;
40 ret++;
41 }
42 return ret+tot[1]+tot[2]+tot[3]+tot[4];
43 }
44 void dfs(int nw){
45 if(nw>=ans)return;
46 ans=min(ans,nw+gao());
47 for(int i=3,j;i<=14;i++){
48 j=i;
49 while(num[j]>=3&&j<=14)j++;
50 if(j-i>=2){
51 for(int k=i+1;k<j;k++){
52 for(int l=i;l<=k;l++){
53 num[l]-=3;
54 }
55 dfs(nw+1);
56 for(int l=i;l<=k;l++){
57 num[l]+=3;
58 }
59 }
60 }
61 }
62 for(int i=3,j;i<=14;i++){
63 j=i;
64 while(num[j]>=2&&j<=14)j++;
65 if(j-i>=3){
66 for(int k=i+2;k<j;k++){
67 for(int l=i;l<=k;l++){
68 num[l]-=2;
69 }
70 dfs(nw+1);
71 for(int l=i;l<=k;l++){
72 num[l]+=2;
73 }
74 }
75 }
76 }
77 for(int i=3,j;i<=14;i++){
78 j=i;
79 while(num[j]&&j<=14)j++;
80 if(j-i>=5){
81 for(int k=i+4;k<j;k++){
82 for(int l=i;l<=k;l++){
83 num[l]--;
84 }
85 dfs(nw+1);
86 for(int l=i;l<=k;l++){
87 num[l]++;
88 }
89 }
90 }
91 }
92 }
93 int main(){
94 scanf("%d%d",&t,&n);
95 while(t--){
96 ans=inf;
97 memset(num,0,sizeof(num));
98 for(int i=1;i<=n;i++){
99 scanf("%d%d",&x,&y);
100 if(!x)x=16;
101 if(x==1)x=14;
102 if(x==2)x=15;
103 num[x]++;
104 }
105 dfs(0);
106 printf("%d\n",ans);
107 }
108 return 0;
109 }