题意

​LOJ传送门​

题解

可以发现「七对子」 和 「国士无双」直接暴力就行了。

唯一的就是剩下的"3*4+2"。

考试的时候写了个爆搜剪枝,开了O2有50pts。写的时候发现可以DP,但是没写。

然后下来写了发现就4个转移。。。


「GXOI / GZOI2019」宝牌一大堆 (DP)_#include

表示当考虑前

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_02

张牌,有

「GXOI / GZOI2019」宝牌一大堆 (DP)_#include_03

个雀头,

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_04

个面子,

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_05

用了

「GXOI / GZOI2019」宝牌一大堆 (DP)_#include_06

张,

「GXOI / GZOI2019」宝牌一大堆 (DP)_爆搜_07

用了

「GXOI / GZOI2019」宝牌一大堆 (DP)_#include_08

张,

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_02

用了

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_10

张时,前

「GXOI / GZOI2019」宝牌一大堆 (DP)_#include_11

张牌的最大分数。

注意这里是"前

「GXOI / GZOI2019」宝牌一大堆 (DP)_#include_11

张牌的最大分数",也就是没有计算

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_13

这最后

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_14

种牌的贡献。

有个性质就是杠子一定不会选,因为选杠子一定劣于面子。

因为

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_15

,就算这张牌是宝牌,杠子的贡献是

「GXOI / GZOI2019」宝牌一大堆 (DP)_c++_16

所以我们没有考虑杠子。

上代码

CODE

luogu上开了O2才过

#include <bits/stdc++.h>
using namespace std;
inline int getid(char *s) {
if(isdigit(s[0])) {
int re = s[0]-'0';
if(s[1] == 'm') return re;
if(s[1] == 'p') return 9+re;
if(s[1] == 's') return 18+re;
}
if(s[0] == 'E') return 28;
if(s[0] == 'S') return 29;
if(s[0] == 'W') return 30;
if(s[0] == 'N') return 31;
if(s[0] == 'Z') return 32;
if(s[0] == 'B') return 33;
if(s[0] == 'F') return 34;
}
typedef long long LL;
int cnt[50], bel[50];
LL ans, c[5][5], pw6[10];
bool bao[50];

int arr13[14] = { 0, 1, 9, 10, 18, 19, 27, 28, 29, 30, 31, 32, 33, 34 }; //指定的13张牌的编号
void solve13() {
bool flg = 1;
for(int i = 1; i <= 13; ++i) flg &= bool(cnt[arr13[i]]);
if(flg) {
LL now = 13;
for(int i = 1; i <= 13; ++i)
now = now * c[cnt[arr13[i]]][1] * (bao[arr13[i]] ? 2 : 1);
for(int i = 1; i <= 13; ++i)
ans = max(ans, now / c[cnt[arr13[i]]][1] * c[cnt[arr13[i]]][2] * (bao[arr13[i]] ? 2 : 1));
}
}

void dfs(int i, int j, LL now) { //暴力枚举七对子
if(j == 7) { ans = max(now, ans); return; }
if(i > 34) return;
LL tmp = now*pw6[7-j]*(1ll<<((7-j)<<1)); //剪枝,预估之后的最大值,如果小于等于当前答案就不用了搜下去了
if(tmp <= ans) return;
if(cnt[i] >= 2) {
cnt[i] -= 2;
dfs(i+1, j+1, now*c[cnt[i]+2][2]*(bao[i]?4:1));
cnt[i] += 2;
}
if(tmp <= ans) return;
dfs(i+1, j, now);
}
void solve7() { dfs(1, 0, 7); }

LL dp[36][2][5][5][5][5];
inline void upd(LL &x, LL y) { x = y > x ? y : x; }
inline int calc(int i, int j) { return i > 0 ? c[cnt[i]][j] * (bao[i] ? (1<<j) : 1) : 1; } //计算i选j张的贡献
void solve() {
memset(dp, 0, sizeof dp);
dp[1][0][0][0][0][0] = 1;
for(int i = 1; i <= 34; ++i)
for(int j = 0; j < 2; ++j)
for(int k = 0; k <= 4; ++k)
for(int a = 0; a <= 4; ++a)
for(int b = 0; b <= 4; ++b)
for(int c = 0; c <= 4; ++c) {
LL val = dp[i][j][k][a][b][c];
if(val) {
upd(dp[i+1][j][k][b][c][0], val * calc(i-2, a));
if(!j && cnt[i]-c >= 2)
upd(dp[i][j+1][k][a][b][c+2], val);
if(k<4 && cnt[i]-c >= 3)
upd(dp[i][j][k+1][a][b][c+3], val);
if(k<4 && bel[i] && i>2 && bel[i]==bel[i-2] && cnt[i]-c >= 1 && cnt[i-1]-b >= 1 && cnt[i-2]-a >= 1)
upd(dp[i][j][k+1][a+1][b+1][c+1], val);
}
}
for(int a = 0; a <= 4; ++a)
for(int b = 0; b <= 4; ++b)
for(int c = 0; c <= 4; ++c) //最后答案算上a,b,c
upd(ans, dp[34][1][4][a][b][c] * calc(32, a) * calc(33, b) * calc(34, c));
}

int main () {
//freopen("doraippai.in", "r", stdin);
// freopen("doraippai.out", "w", stdout);
for(int i = 1; i <= 9; ++i) bel[i] = 1;
for(int i = 10; i <= 18; ++i) bel[i] = 2;
for(int i = 19; i <= 27; ++i) bel[i] = 3;
c[0][0] = 1;
for(int i = 1; i <= 4; ++i) {
c[i][0] = c[i][i] = 1;
for(int j = 1; j < i; ++j)
c[i][j] = c[i-1][j-1] + c[i-1][j];
}
pw6[0] = 1;
for(int i = 1; i <= 7; ++i) pw6[i] = 6ll * pw6[i-1];
int T; scanf("%d", &T); while(T--) {
for(int i = 1; i <= 34; ++i)
cnt[i] = 4, bao[i] = 0;
char s[5];
while(scanf("%s", s), s[0] != '0') {
int num = getid(s);
--cnt[num];
}
while(scanf("%s", s), s[0] != '0') {
int num = getid(s);
bao[num] = 1;
}
ans = 0;
solve13();
solve7();
solve();
printf("%lld\n", ans);
}
}