[NOIp2015]斗地主
Description
Luogu2688
给定一手牌,问最少几步把这手牌打完。
Solution
DP出散牌,暴力找顺子。
由于顺子可能会出现多余的单牌,所以暴力的枚举如何出顺子才更优。
DP就是一个暴力DP,没有什么有趣的地方,不过不要忘了拆牌。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min;
const int N = 25;
int f[N][N][N][N][3];
int mx[N];
int a[N];
int my[N];
int ans = 1000, n;
void init() {
f[0][0][0][0][0] = 0;
for (int r = 0; r <= mx[5]; ++r) {
for (int l = 0; l <= mx[4]; ++l) {
for (int k = 0; k <= mx[3]; ++k) {
for (int j = 0; j <= mx[2]; ++j) {
for (int i = 0; i <= mx[1]; ++i) {
int x = 1000;
// 出散牌:
if (i) x = min(x, f[i-1][j][k][l][r]);
if (j) x = min(x, f[i][j-1][k][l][r]);
if (k) x = min(x, f[i][j][k-1][l][r]);
if (l) x = min(x, f[i][j][k][l-1][r]);
if (r) x = min(x, f[i][j][k][l][r-1]);
if (r>1) x = min(x, f[i][j][k][l][r-2]);
// 三带/拆三
if (k) {
if (i) x = min(x, f[i-1][j][k-1][l][r]);
if (j) x = min(x, f[i][j-1][k-1][l][r]);
if (r) x = min(x, f[i][j][k-1][l][r-1]);
if (r>1) x = min(x, f[i][j][k-1][l][r-2]);
x = min(x, f[i+1][j+1][k-1][l][r] - 1);
}
// 四带/拆四
if (l) {
if (i>1) x = min(x, f[i-2][j][k][l-1][r]);
if (i && r) x = min(x, f[i-1][j][k][l-1][r-1]);
if (r>1) x = min(x, f[i][j][k][l-1][r-2]);
if (j>1) x = min(x, f[i][j-2][k][l-1][r]);
if (j && r>1) x = min(x, f[i][j-1][k][l-1][r-2]);
if (j) x = min(x, f[i][j-1][k][l-1][r]);
if (l>1) x = min(x, f[i][j][k][l-2][r]);
x = min(x, f[i+1][j][k+1][l-1][r] - 1);
}
f[i][j][k][l][r] = min(x + 1, f[i][j][k][l][r]);
}
}
}
}
}
}
void dfs(int x) {
if (x > ans) return;
for (int k = 1, flag; k <= 3; ++k) {
for (int i = 1; i <= 12; ++i) {
flag = 1;
int len = 4 / k + 1;
while (flag && i + len - 1 <= 12) {
for (int j = 1; j <= len; ++j) {
if (a[i+j-1] < k) {
flag = 0;
break;
}
}
if (!flag) break;
for (int j = 1; j <= len; ++j) {
a[i+j-1] -= k;
}
dfs(x+1);
for (int j = 1; j <= len; ++j) {
a[i+j-1] += k;
}
len++;
}
}
}
memset(my, 0, sizeof my);
for (int i = 1; i <= 13; ++i) my[a[i]]++;
my[5] = a[14];
ans = min(ans, x + f[my[1]][my[2]][my[3]][my[4]][my[5]]);
}
int main() {
memset(f, 0x3f, sizeof f);
int t;
scanf("%d%d\n", &t, &n);
mx[1] = n; mx[2] = n / 2; mx[3] = n / 3; mx[4] = n / 4; mx[5] = 2;
init();
while (t--) {
ans = 1000;
memset(a, 0, sizeof a);
for (int i = 1, x, y; i <= n; ++i) {
scanf("%d%d", &x, &y);
if (x == 0) a[14]++;
else if (x <= 2) a[11+x]++;
else a[x-2]++;
}
dfs(0);
printf("%d\n", ans);
}
return 0;
}