洛谷P10504 守卫者的挑战 题解 概率DP
题目链接:https://www.luogu.com.cn/problem/P10504
状态 \(f_{i, s, k}\) 表示:
- 当前正面临第 \(i\) 项挑战(此时第 \(1 \sim i-1\) 项挑战已完成,第 \(i\) 项挑战还没开始);
- 目前已经挑战成功了 \(s\) 项(即第 \(1 \sim i-1\) 项挑战中共有 \(s\) 项挑战成功,\((i-1)-s\) 项没挑战成功);
- 此时的背包容量剩余 \(k\)(实际实现时加了一个 \(210\) 的偏移)。
边界条件时 \(i = n+1\),此时第 \(1\) 至 \(i-1 = n\) 项挑战均已挑战完成。
此时若 \(s \ge L\)(至少挑战成功 \(L\) 次) 且 \(k \ge 0\)(背包足够装所有的地图残片),则获胜的概率为 \(1\);否则,获胜的概率为 \(0\)。
非边界情况下:
\(f_{i, s, k}\) 有 \(p_i\) 的概率转移到 \(f_{i+1, s+1, k + a_i}\),有 \(1 - p_i\) 的概率转移到 \(f_{i+1, s, k}\),所以:
\[f_{i, s, k} = p_i \times f_{i+1, s+1, k + a_i} + (1 - p_i) \times f_{i+1, s, k}
\]
示例程序:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 220;
bool vis[maxn][maxn][maxn*2];
double f[maxn][maxn][maxn*2];
int n, L, K, a[maxn];
double p[maxn];
// 当前在第 i 局,目前已经赢了 s 局,剩余背包容量为 k
double dfs(int i, int s, int k) {
k = min(k, maxn*2-1);
if (i == n+1) {
return k >= 210 && s >= L ? 1 : 0;
}
if (vis[i][s][k])
return f[i][s][k];
vis[i][s][k] = true;
return f[i][s][k] = p[i] * dfs(i+1, s+1, k + a[i]) + (1 - p[i]) * dfs(i+1, s, k);
}
int main() {
cin >> n >> L >> K;
for (int i = 1; i <= n; i++) {
cin >> p[i];
p[i] /= 100.0;
}
for (int i = 1; i <= n; i++)
cin >> a[i];
double res = dfs(1, 0, K + 210); // 初始背包容量为 K
printf("%.6lf\n", res);
return 0;
}