题目地址:CF167B - Wizards and Huge Prize - 洛谷 ↗
最开始,你有 M 的容积。有 n 轮比赛,对于第 i 场比赛,你获胜的概率为 pi.
比赛的奖励分为两种:一种是增加 ai 的容积,一种是给你 1 个奖品。
一种赢比赛的合法方案,需要满足:n 轮比赛结束时,总容积 ≥ 获得的奖品数量。
求获胜场次 ≥l,且方案合法的概率。
1≤M,n,ai≤200,0≤pi≤1.
概率 DP。
显然,获得一个奖品等价于容积 −1,方案合法 ⇔ n 轮结束后的容积非负。
设 dpi,j,k 表示:前 i 轮比赛后,在其中的 j 场中获胜,当前可用容积为 k 发生的概率。
为了方便表示,允许下标为负,则:
Ans=i=l∑nj=0∑+∞dpn,i,j
第三维数组的大小?
注意到,每次操作最多减 1,如果容积 >=n ,就不可能不合法了,都和 n 的状态等价。
所以有意义的可用容积在 [−n,n] 范围内,空间开 2×n 即可,答案的表达式也可以改写为:
Ans=i=l∑nj=0∑ndpn,i,j
考虑如何转移?
刷表,状态 (i,j,k) 只会贡献到 (i+1,j,k) 或 (i+1,j+1,min{n,k+ai+1}).
两种转移分别对应 赢/不赢 第 i+1 轮,具体地:
(1−pi+1)×dpi,j,kpi+1×dpi,j,k→→dpi+1,j,kdpi+1,j+1,min{n,k+ai+1}
时空复杂度均为 O(n3).
实现时,注意将第三维坐标整体 +n.
const int N = 200;
dp[0][0][N + K] = 1.0;
for(int i = 0; i < n; i++) {
for(int j = 0; j <= i; j++) {
for(int k = 0; k <= (N << 1); k++) {
dp[i + 1][j][k] += (1.0 - p[i + 1]) * dp[i][j][k];
int nxt = min((N << 1), k + a[i + 1]);
if(nxt >= 0) dp[i + 1][j + 1][nxt] += p[i + 1] * dp[i][j][k];
}
}
}
for(int i = l; i <= n; i++) {
for(int j = N; j <= (N << 1); j++) {
ans += dp[n][i][j];
}
}
cpp