题目地址:P5301 [GXOI/GZOI2019]宝牌一大堆
这里是官方题解(by lydrainbowcat)
部分分
直接搜索可以得到暴力分,因为所有和牌方案一共只有一千万左右,稍微优化一下数据少的测试点可以跑过
\(3\) ~ \(7\) 已经打出的,不需要考虑顺子,可以跟七对子类似直接算
正解
预处理组合数
DP 计算 \(3*4+2\) :
前 \(i\) 种牌,选了 \(j\) 组面子, \(k\) 组雀头,其中第 \(i - 2\) ~ \(i\) 种牌分别选了 \(l,m,n\) 张时,前 \(i - 3\) 种牌可以获得的最大价值
转移:刻子、顺子、杠子、雀头四个转移,详见 std 中 calc_normal 函数的注释
直接计算七对子、国士无双
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<ctime>
using namespace std;
// 1m 2m ... 9m 1p 2p ... 9p 1s 2s ... 9s E S W N Z B F
const bool shuntsu[35] = { 0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0 };
const int yao[13] = { 1,9,10,18,19,27,28,29,30,31,32,33,34 };
int a[35], b[35], c[5][5], d[35];
long long f[35][5][2][5][5][5], chitoi[35][8], ans;
int label(char str[3]) {
if (str[0] == 'E') return 28;
if (str[0] == 'S') return 29;
if (str[0] == 'W') return 30;
if (str[0] == 'N') return 31;
if (str[0] == 'Z') return 32;
if (str[0] == 'B') return 33;
if (str[0] == 'F') return 34;
if (str[1] == 'm') return str[0] - '0';
if (str[1] == 'p') return 9 + str[0] - '0';
if (str[1] == 's') return 18 + str[0] - '0';
return 0;
}
inline void update(long long &dst, long long src) {
dst = max(dst, src);
}
inline int dora(int idx, int cnt) {
return d[idx] ? 1 << cnt : 1;
}
long long calc_normal() {
long long ret = 0;
memset(f, 0, sizeof(f));
f[1][0][0][0][0][0] = 1;
// 前i种牌,选了j组面子,k组雀头,其中第i-2~i种牌分别选了l,m,n张时,前i-3种牌可以获得的最大价值
for (int i = 1; i <= 34; i++)
for (int j = 0; j <= 4; j++)
for (int k = 0; k <= 1; k++)
for (int l = 0; l <= 4; l++)
for (int m = 0; m <= 4; m++)
for (int n = 0; n <= 4; n++) {
long long now;
if ((now = f[i][j][k][l][m][n]) == 0) continue;
// 去选下一种牌
if (i < 34)
update(f[i + 1][j][k][m][n][0], now * (i > 2 ? c[a[i - 2]][l] : 1) * dora(i - 2, l));
// 组成刻子
if (j < 4 && a[i] - n >= 3)
update(f[i][j + 1][k][l][m][n + 3], now);
// 组成杠子,杠子也可以算作面子来统计
if (j < 4 && a[i] - n >= 4)
update(f[i][j + 1][k][l][m][n + 4], now);
// 组成顺子
if (j < 4 && shuntsu[i] && a[i] - n && a[i - 1] - m && a[i - 2] - l)
update(f[i][j + 1][k][l + 1][m + 1][n + 1], now);
// 组成雀头
if (k < 1 && a[i] - n >= 2)
update(f[i][j][k + 1][l][m][n + 2], now);
if (i == 34 && j == 4 && k == 1)
update(ret, now * c[a[i]][n] * c[a[i - 1]][m] * c[a[i - 2]][l] * dora(i, n) * dora(i - 1, m) * dora(i - 2, l));
}
return ret;
}
long long calc_chitoi() {
memset(chitoi, 0, sizeof(chitoi));
chitoi[0][0] = 1;
for (int i = 1; i <= 34; i++)
for (int j = 0; j <= 7; j++) {
if (chitoi[i - 1][j] == 0) continue;
update(chitoi[i][j], chitoi[i - 1][j]);
if (j < 7) update(chitoi[i][j + 1], chitoi[i - 1][j] * c[a[i]][2] * dora(i, 2));
}
return chitoi[34][7] * 7;
}
long long calc_kokushi() {
long long ret = 0;
for (int i = 0; i < 13; i++) {
if (a[yao[i]] == 0) return 0;
if (a[yao[i]] == 1) continue;
long long temp = c[a[yao[i]]][2] * dora(yao[i], 2);
for (int j = 0; j < 13; j++) {
if (i == j) continue;
temp = temp * a[yao[j]] * dora(yao[j], 1);
}
update(ret, temp * 13);
}
return ret;
}
/*void dfs(int x, int m, int n) {
if (m == 4 && n == 1 || m == 0 && n == 7) {
long long temp = n;
for (int i = 1; i <= 34; i++)
if (a[i] < b[i]) temp = temp * c[b[i]][b[i] - a[i]] * dora(i, b[i] - a[i]);
update(ans, temp);
return;
}
if (x > 34) return;
if (n > 1 && m) return;
dfs(x + 1, m, n);
if (m < 4 && a[x] >= 4) { // 杠
a[x] -= 4;
dfs(x, m + 1, n);
a[x] += 4;
}
if (m < 4 && a[x] >= 3) { // 刻
a[x] -= 3;
dfs(x, m + 1, n);
a[x] += 3;
}
if (n < 1 && a[x] >= 2) { // 雀头
a[x] -= 2;
dfs(x, m, n + 1);
a[x] += 2;
}
if (m<= 4 && shuntsu[x] && a[x] && a[x - 1] && a[x - 2]) { // 顺子
a[x] -= 1, a[x - 1] -= 1, a[x - 2] -= 1;
dfs(x, m + 1, n);
a[x] += 1, a[x - 1] += 1, a[x - 2] += 1;
}
if (m == 0 && a[x] >= 2 && n > 0 && n < 7) {
a[x] -= 2;
dfs(x + 1, m, n + 1);
a[x] += 2;
}
}*/
int main() {
for (int i = 0; i <= 5; i++) c[i][0] = 1;
for (int i = 1; i <= 5; i++)
for (int j = 1; j <= 5; j++)
c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
char str[3];
int T;
cin >> T;
while (T--) {
for (int i = 1; i <= 34; i++) a[i] = 4;
while (scanf("%s", str) != EOF && str[0] != '0') a[label(str)]--;
memset(d, 0, sizeof(d));
while (scanf("%s", str) != EOF && str[0] != '0') d[label(str)] = 1;
// ans = 0;
// for (int i = 1; i <= 34; i++) b[i] = a[i];
// dfs(1, 0, 0);
// if (ans != calc_normal() && ans != calc_chitoi()) while (1);
ans = 0;
update(ans, calc_normal());
update(ans, calc_chitoi());
update(ans, calc_kokushi());
cout << ans << endl;
}
}