刚开始读到题目的时候,非常懵逼,非常崩溃,写着写着呢,也有点崩溃,细节有点多。
这个做法呢,只能过掉官方数据,洛谷上好像有加强版,只能得$86$分,就没有管了。
大概说一下思路:
暴力搜索每一种可能的情况,如果可以就递归下去,然后回溯。
搜索框架的话,大概就是把当前搜到的出牌次数传到参数里面,如果参数已经大于了当前的最小答案就剪枝退出。然后在当前状态下枚举每一种出牌的情况,能够出就出,然后递归到下一层,参数出牌次数+1。
可以证明不会无限递归下去,因为在参数已经大于了当前的最小答案的时候就退出了。
可以分为三大类出牌:顺子、带牌、散牌
还有一些细节什么的:
1.把$A$放在$K$后面,顺子的时候比较方便
2.$2$不能在顺子里面(当时还理解了好久$2$点是什么,还以为是什么其他代称...)
3.王。之前以为王只可以单出或者火箭,没有认真读题,王是可以被带的,但是只能带单牌,王不能看成一对,他们牌值不一样。
4.后来发现顺子和带牌打完之后,可以不用单独考虑打散牌(单牌,对子,三张牌),因为如果剩下,$1$次就可以打出去一种,而且无论如何只打$1$次用的打牌次数会最少。而且散牌不用考虑与其他牌的组合。(可以感性理解,次数最多肯定是每一次都按种数打散牌)

1 #include<cstdio>
2 #include<algorithm>
3 #include<vector>
4 #include<cstring>
5 #include<queue>
6 #include<map>
7 #include<iostream>
8 using namespace std;
9 #define ll long long
10 #define INF 0x3f3f3f3f
11 #define N 30
12 int rd()
13 {
14 int f=1,s=0;char c=getchar();
15 while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
16 while(c>='0'&&c<='9'){s=(s<<3)+(s<<1)+(c^48);c=getchar();}
17 return f*s;
18 }
19 //J->11 Q->12 K->13 A->14 2->15 King->16
20 //王特殊处理 只能出火箭或者单出(糟糕 还可以单带
21 //2不能出顺子
22 int n,ans;
23 int cnt[N];
24 void dfs(int res)
25 {
26 if(res>=ans) return ;//剪枝 提前退出
27 /*
28 //-----炸弹
29 for(int i=3;i<=15;i++)
30 if(cnt[i]>=4)
31 {
32 cnt[i]-=4;
33 dfs(res+1);
34 cnt[i]+=4;
35 }
36 //-----
37 //-----对子牌
38 for(int i=3;i<=15;i++)
39 if(cnt[i]>=2)
40 {//2个对子...就是炸弹了 应该在前面判断过了
41 cnt[i]-=2;
42 dfs(res+1);
43 cnt[i]+=2;
44 }
45 //-----
46 //-----三张牌
47 for(int i=3;i<=15;i++)
48 if(cnt[i]>=3)
49 {
50 cnt[i]-=3;
51 dfs(res+1);
52 cnt[i]+=3;
53 }
54 //-----
55 */
56 //-----顺子
57 //---单顺子
58 int k=0;
59 for(int i=3;i<=14;i++)//不包括2点
60 {
61 if(cnt[i]==0) k=0;//顺子断了 从这里重新开始
62 else//否则就接下去
63 {
64 k++;
65 if(k>=5)
66 {
67 for(int j=i;j>=i-k+1;j--)
68 cnt[j]--;
69 dfs(res+1);
70 for(int j=i;j>=i-k+1;j--)
71 cnt[j]++;
72 }
73 }
74 }
75 //---
76 //---双顺子
77 k=0;
78 for(int i=3;i<=14;i++)//不包括2点
79 {
80 if(cnt[i]<2) k=0;//顺子断了 从这里重新开始
81 else//否则就接下去
82 {
83 k++;
84 if(k>=3)
85 {
86 for(int j=i;j>=i-k+1;j--)
87 cnt[j]-=2;
88 dfs(res+1);
89 for(int j=i;j>=i-k+1;j--)
90 cnt[j]+=2;
91 }
92 }
93 }
94 //---
95 //---三顺子
96 k=0;
97 for(int i=3;i<=14;i++)//不包括2点
98 {
99 if(cnt[i]<3) k=0;//顺子断了 从这里重新开始
100 else//否则就接下去
101 {
102 k++;
103 if(k>=2)
104 {
105 for(int j=i;j>=i-k+1;j--)
106 cnt[j]-=3;
107 dfs(res+1);
108 for(int j=i;j>=i-k+1;j--)
109 cnt[j]+=3;
110 }
111 }
112 }
113 //---
114 //-----
115 //-----带牌
116 for(int i=3;i<=15;i++)
117 {
118 if(cnt[i]<3) continue;
119 //3带X
120 cnt[i]-=3;
121 for(int j=3;j<=16;j++)
122 {
123 if(i==j) continue;
124 if(cnt[j]>=1)
125 {
126 cnt[j]--;
127 dfs(res+1);
128 cnt[j]++;
129 }
130 if(cnt[j]>=2&&j!=16/*王不能一起带*/)
131 {
132 cnt[j]-=2;
133 dfs(res+1);
134 cnt[j]+=2;
135 }
136 }
137 cnt[i]+=3;
138 //4带X(两个单张或两个对)
139 if(cnt[i]<4) continue;
140 cnt[i]-=4;
141 for(int j=3;j<=16;j++)
142 {
143 if(i==j||cnt[j]<=0) continue;
144 cnt[j]--;//两张单牌
145 for(int k=3;k<=16;k++)
146 {
147 if(k==j||k==i||cnt[k]<1) continue;
148 cnt[k]--;
149 dfs(res+1);
150 cnt[k]++;
151 }
152 cnt[j]++;
153 if(cnt[j]<2||j==16/*王不能一起*/) continue;
154 cnt[j]-=2;//两对对子
155 for(int k=3;k<=15;k++)
156 {
157 if(k==j||k==i||cnt[k]<2) continue;
158 cnt[k]-=2;
159 dfs(res+1);
160 cnt[k]+=2;
161 }
162 cnt[j]+=2;
163 }
164 cnt[i]+=4;
165 }
166 //-----
167 for(int i=3;i<=16;i++)
168 if(cnt[i]) res++;
169 //突然发现 单张 对子三张都不用特殊考虑 反正能一次出完剩下的所有牌
170 ans=min(ans,res);
171 }
172 int main()
173 {
174 int T=rd(),n=rd();
175 while(T--)
176 {
177 memset(cnt,0,sizeof(cnt));
178 ans=INF;
179 for(int i=1;i<=n;i++)
180 {
181 int m=rd(),opt=rd();
182 if(m==0) m=16;
183 if(m==1) m=14;
184 if(m==2) m=15;
185 cnt[m]++;
186 }
187 dfs(0);
188 printf("%d\n",ans);
189 }
190 return 0;
191 }
