「ICPC NEF 2025 J」Jinx or Jackpot

概率期望,是我做不出来的,要强化下这方面的了,不然联赛考到就是爆掉。然后提一嘴,官方题解写得很好,居然能让我看懂。

首先考虑朴素 dp,发现玩了几次后赢或者输的顺序 Jack 并不关心,所以有效信息就有当前赢、输了多少次,Jack 手上有多少块钱,于是定义 \(dp_{a, b, x}\) 表示 Jack 当前赢了 \(a\) 次,输了 \(b\) 次,持有 \(x\) 块钱,接下来收益期望的最大值,再令 \(p(a, b)\) 表示已经赢了 \(a\) 次,输了 \(b\) 次的情况下下一次赢的概率,就可以写出 dp 的转移:\(dp_{a, b, x} \leftarrow \max\limits_{y \in [0, x] \cap \mathbb{Z}} {p(a, b) dp_{a + 1, b, x + y} + (1 - p(a, b)) dp_{a, b + 1, x - y}}\),边界为 \(dp_{a, k - a, x} \leftarrow x\)

这个取 \(\max\) 让我们非常的不爽啊,所以去想想每一次拿钱有没有什么性质,理性来说,我们按 \(a + b\)\(k\)\(0\) 归纳可以发现我们每次要么 all in,要么不抽,感性理解的话,如果下一部分进机子的话剩下的钱我们是没用上的,要么概率 \(\geq \frac{1}{2}\) 不优于 all in,要么概率 \(< \frac{1}{2}\) 劣于不抽,就类似于一个下凸单峰函数,所以 dp 时我们只需要抉择 all in 还是不抽。

所以 dp 转移变为 \(dp_{a, b, x} \leftarrow \max(p(a, b) dp_{a + 1, b, 2x}, p(a, b) dp_{a + 1, b, x} + (1 - p(a, b)) dp_{a, b + 1, x})\),这里观察式子,发现第三维 \(x\) 要么 \(\times 2\) 要么不变,而 \(\times 2\) 在结果上看就是将期望 \(\times 2\),所以可以省略 \(x\) 这一维,于是有转移式 \(dp_{a, b} \leftarrow max(2p(a, b) dp_{a + 1, b}, p(a, b) dp_{a + 1, b} + (1 - p(a, b)) dp_{a, b + 1}))\),边界状态 \(dp_{a, k - a} \leftarrow 1000\)

现在只需要考虑怎么计算 \(p(a, b)\),即发生事件“赢 \(a\) 次输 \(b\) 次”后下一次赢的概率,条件概率试一下贝叶斯公式,赢 \(a\) 次输 \(b\) 次的概率为 \(\frac{\binom{a + b}{a} \sum\limits_{i = 1}^n p_i^a (1 - p_i)^b}{n}\),赢 \(a + 1\) 次输 \(b\) 次且最后一次赢的概率为 \(\frac{\binom{a + b}{a} \sum\limits_{i = 1}^n p_i^a (1 - p_i)^b \cdot p_i}{n} = \frac{\binom{a + b}{a} \sum\limits_{i = 1}^n p_i^{a + 1} (1 - p_i)^b}{n}\),那么 \(p(a, b) = \frac{\binom{a + b}{a} \sum\limits_{i = 1}^n p_i^{a + 1} (1 - p_i)^b}{\binom{a + b}{a} \sum\limits_{i = 1}^n p_i^a (1 - p_i)^b} = \frac{\sum\limits_{i = 1}^n p_i^{a + 1} (1 - p_i)^b}{\sum\limits_{i = 1}^n p_i^a (1 - p_i)^b}\)\(100 \cdot k^2\)预处理即可。

时间复杂度:\(O(N + 100 \cdot k^2)\)

/*
address:https://codeforces.com/problemset/problem/2181/J
AC 2026/9/9 21:45
*/
#include<bits/stdc++.h>
using namespace std;
typedef long double ld;
const int N = 1e5 + 5, K = 32;
ld dp[K][K];
ld p[K][K], pw[105][K], pl[105][K];
int cnt[105];
int n, k;
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1, P;i <= n;++i) scanf("%d", &P), ++cnt[P];
    for (int i = 0;i <= 100;++i) {
        pw[i][0] = 1, pl[i][0] = 1;
        ld win = ld(i) / 100, lose = 1 - win;
        for (int j = 1;j <= k;++j)
            pw[i][j] = pw[i][j - 1] * win,
            pl[i][j] = pl[i][j - 1] * lose;
    }
    for (int a = 0;a <= k;++a)
        for (int b = 0;b <= k;++b) {
            ld x = 0, y = 0;
            for (int i = 0;i <= 100;++i)
                x += pw[i][a + 1] * pl[i][b] * cnt[i],
                y += pw[i][a] * pl[i][b] * cnt[i];
            p[a][b] = y > 0 ? x / y : 1;
        }
    for (int a = 0;a <= k;++a) dp[a][k - a] = 1000;
    for (int sum = k - 1;sum >= 0;--sum)
        for (int a = 0;a <= sum;++a) {
            int b = sum - a;
            dp[a][b] = max(p[a][b] * dp[a + 1][b] + (1 - p[a][b]) * dp[a][b + 1], 2 * p[a][b] * dp[a + 1][b]);
        }
    printf("%.10Lf\n", dp[0][0] - 1000);
    return 0;
}
posted @ 2026-09-10 14:41  keysky  阅读(5)  评论(0)    收藏  举报