发布于  更新于 

洛谷P2540 斗地主增强版

搜索 题解 OI

一道经典的搜索?题, 早就想做了.

听说考试原题的数据很水, 瞎贪心都能过, 就不说了, 这里分析一下几个直接贪心不能解决的地方.

  • 两个鬼是否拆开(3333鬼鬼)
  • 顺子要截取多少(日常斗地主经验太常见这种情况了)
  • 炸弹和三联拆不拆开(33334444, 理解成四带两对可以一次走掉)
  • 多次顺子/拆炸弹

按照以上几个痛点依次dfs搜索就是了, 具体见代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <iostream>
#include <map>
using namespace std;

typedef long long int64;

const int INF = 0x3f3f3f3f;
const int MAXN = 25;

typedef int card;

const card three = 1;
const card four = 2;
const card five = 3;
const card six = 4;
const card seven = 5;
const card eight = 6;
const card nine = 7;
const card ten = 8;
const card J = 9;
const card Q = 10;
const card K = 11;
const card A = 12;
const card two = 13;
const card grey_joker = 14;
const card red_joker = 15;

card parse(int a, int b) {
switch (a) {
case 0:
if (b == 1)
return grey_joker;
else
return red_joker;
case 1:
return A;
case 2:
return two;
case 3:
return three;
case 4:
return four;
case 5:
return five;
case 6:
return six;
case 7:
return seven;
case 8:
return eight;
case 9:
return nine;
case 10:
return ten;
case 11:
return J;
case 12:
return Q;
case 13:
return K;
default:
clog << "fuck unknown card" << endl;
return 0;
}
}

int n;
int card_count[20];
int style_count[5];

// 考虑最后的情况(贪心匹配)
int get_other() {
int tmp[5];
memcpy(tmp, style_count, sizeof tmp);
int ans = 0;
while (tmp[4] > 0) {
tmp[4]--;
ans++;
if (tmp[1] >= 2) { // 带两张单牌
tmp[1] -= 2;
} else if (tmp[2] >= 2) { // 带两对
tmp[2] -= 2;
} else if (tmp[2] >= 1) { // 带一对
tmp[2]--;
}
}
while (tmp[3] > 0) {
tmp[3]--;
ans++;
if (tmp[1] >= 1) { // 带一张单牌
tmp[1]--;
} else if (tmp[2] >= 1) { // 带一对
tmp[2]--;
}
}
ans += tmp[2] + tmp[1];
return ans;
}

// 考虑拆开对子和三连, 炸弹
int dfs_break() {
int ans = INF;

// 不拆
ans = min(ans, get_other());

// 拆对子 (没意义)
// if (style_count[2] > 0) {
// style_count[2]--;
// style_count[1] += 2;
// ans = min(ans, get_other());
// style_count[1] -= 2;
// style_count[2]++;
// }

// 拆三联
if (style_count[3] > 0) {
style_count[3]--;
style_count[2]++;
style_count[1]++;
ans = min(ans, dfs_break());
style_count[1]--;
style_count[2]--;
style_count[3]++;
}

// 拆炸弹
if (style_count[4] > 0) {
style_count[4]--;

style_count[2] += 2;
ans = min(ans, dfs_break());
style_count[2] -= 2;

style_count[1]++;
style_count[3]++;
ans = min(ans, dfs_break());
style_count[3]--;
style_count[1]--;

style_count[4]++;
}

return ans;
}

// 考虑三种顺子
int dfs_sequence() {
int ans = INF;

// 不出顺子
memset(style_count, 0, sizeof style_count);
for (card i = three; i <= red_joker; i++) {
style_count[card_count[i]]++;
}
ans = min(ans, dfs_break());

// 考虑单顺子
for (card i = three; i + 4 < two; i++) {
for (card j = i; j < two; j++) {
if (card_count[j] < 1) break;
if (j - i < 4) continue;
for (card k = i; k <= j; k++) {
card_count[k] -= 1;
}
ans = min(ans, dfs_sequence() + 1);
for (card k = i; k <= j; k++) {
card_count[k] += 1;
}
}
}

// 考虑双顺子
for (card i = three; i + 2 < two; i++) {
for (card j = i; j < two; j++) {
if (card_count[j] < 2) break;
if (j - i < 2) continue;
for (card k = i; k <= j; k++) {
card_count[k] -= 2;
}
ans = min(ans, dfs_sequence() + 1);
for (card k = i; k <= j; k++) {
card_count[k] += 2;
}
}
}

// 考虑三顺子
for (card i = three; i + 1 < two; i++) {
for (card j = i; j < two; j++) {
if (card_count[j] < 3) break;
if (j - i < 1) continue;
for (card k = i; k <= j; k++) {
card_count[k] -= 3;
}
ans = min(ans, dfs_sequence() + 1);
for (card k = i; k <= j; k++) {
card_count[k] += 3;
}
}
}

return ans;
}

// 是否出对鬼
int dfs_joker() {
int ans = INF;
if (card_count[grey_joker] > 0 && card_count[red_joker] > 0) {
card_count[grey_joker]--;
card_count[red_joker]--;
ans = min(ans, dfs_sequence() + 1);
card_count[grey_joker]++;
card_count[red_joker]++;
}
ans = min(ans, dfs_sequence());
return ans;
}

int main() {
int tcnt;
cin >> tcnt >> n;
for (int T = 1; T <= tcnt; T++) {
memset(card_count, 0, sizeof card_count);
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
card_count[parse(a, b)]++;
}
cout << dfs_joker() << endl;
}
// ans = INF;
return 0;
}

这题要是放cf educational round上会不会全场fst...