版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/88981526
【题目链接】
【思路要点】
- 首先考虑如何判断一副牌是否胡牌。
- 我们可以用动态规划来解决该问题,可以参考 【CodeForces】CodeForces Global Round 1 题解 一文中 题的解法,我们可以计算一副牌最多可以组成的面子数,再记一维状态表示是否组成过对子即可。
- 回到原题,我们将上述 的状态、数值全部压入状态,看做当前 的状态,经过搜索,这样的状态至多有 个。
- 考虑对于每个权值 ,计算所有大小为 的牌集中不能胡牌的集合数 和总集合数 ,那么 就是权值大于 的概率, 即为权值的期望。
- 记 表示当前处理了 ,共选定了 张牌,胡牌状态为 的集合数,转移只需枚举 的张数即可。
- 时间复杂度 ,其中 。
- 提示:七对子要求对子的大小两两不同。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; const int MAXM = 405; const int MAXS = 4e3 + 5; const int P = 998244353; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct state {int dp[3][3]; }; bool operator < (state a, state b) { for (int i = 0; i <= 2; i++) for (int j = 0; j <= 2; j++) { if (a.dp[i][j] < b.dp[i][j]) return true; if (a.dp[i][j] > b.dp[i][j]) return false; } return false; } int statecnt; map <state, int> mp; state cipher() { state res; for (int i = 0; i <= 2; i++) for (int j = 0; j <= 2; j++) res.dp[i][j] = -1; return res; } state starter() { state res = cipher(); res.dp[0][0] = 0; return res; } state operator + (state a, state b) { state res; for (int i = 0; i <= 2; i++) for (int j = 0; j <= 2; j++) res.dp[i][j] = max(a.dp[i][j], b.dp[i][j]); return res; } state operator + (state now, int num) { state res = cipher(); for (int i = 0; i <= 2 && i <= num; i++) for (int j = 0; j <= 2 && i + j <= num; j++) { if (now.dp[i][j] == -1) continue; int tmp = now.dp[i][j]; for (int k = 0; k <= 2 && i + j + k <= num; k++) chkmax(res.dp[j][k], min(tmp + i + (num - i - j - k) / 3 , 4)); } return res; } void dfs(state now) { if (mp.count(now)) return; mp[now] = ++statecnt; for (int i = 0; i <= 4; i++) dfs(now + i); } typedef pair <pair <state, state>, int> mahjong; //first : without pair, second : with pair bool goal[MAXS]; mahjong states[MAXS]; map <mahjong, int> home; int tots, trans[MAXS][5]; pair <state, state> operator + (pair <state, state> now, int num) { if (num >= 2) return make_pair(now.first + num, (now.second + num) + (now.first + (num - 2))); else return make_pair(now.first + num, now.second + num); } mahjong operator + (mahjong now, int num) { return make_pair(now.first + num, min(now.second + (num >= 2), 7)); } void dfm(mahjong now) { if (home.count(now)) return; home[now] = ++tots; states[tots] = now; for (int i = 0; i <= 4; i++) dfm(now + i); } mahjong inception() { return make_pair(make_pair(starter(), cipher()), 0); } bool finish(mahjong now) { if (now.second >= 7) return true; for (int i = 0; i <= 2; i++) for (int j = 0; j <= 2; j++) if (now.first.second.dp[i][j] >= 4) return true; return false; } void initstates() { dfs(starter()); dfm(inception()); for (int i = 1; i <= tots; i++) { goal[i] = finish(states[i]); for (int j = 0; j <= 4; j++) trans[i][j] = home[states[i] + j]; } } int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void update(int &x, int y) { x += y; if (x >= P) x -= P; } int n, dp[MAXN][MAXM][MAXS]; int binom[5][5], used[MAXN]; int main() { initstates(), read(n); for (int i = 1; i <= 13; i++) { int x, y; read(x), read(y); used[x]++; } for (int i = 0; i <= 4; i++) { binom[i][0] = 1; for (int j = 1; j <= i; j++) binom[i][j] = binom[i - 1][j - 1] + binom[i - 1][j]; } dp[0][0][1] = 1; for (int i = 0; i <= n - 1; i++) for (int j = 0; j <= 4 * i; j++) for (int k = 1; k <= tots; k++) { int tmp = dp[i][j][k]; for (int t = used[i + 1]; t <= 4; t++) update(dp[i + 1][j + t][trans[k][t]], 1ll * tmp * binom[4 - used[i + 1]][t - used[i + 1]] % P); } int ans = 0; for (int i = 13; i <= 4 * n; i++) { int sum = 0, inv = 0; for (int j = 1; j <= tots; j++) { update(sum, dp[n][i][j]); if (!goal[j]) update(inv, dp[n][i][j]); } update(ans, 1ll * inv * power(sum, P - 2) % P); } writeln(ans); return 0; }