【YBTOJ】守卫挑战
题目大意:
有 \(n\) 项任务,有一个数 \(K\)。每一项任务成功的概率是 \(p_i\)(这里与原题目不同,原题目是百分之 \(p'_i\),这里相当于 \(p_i=\frac{p'_i}{100}\)),成功后会使 \(K\) 加上 \(a_i\)。问至少成功 \(l\) 次且最后 \(K\leq0\) 的概率是多少。
数据范围:\(0\leq K\leq2000,0\leq L\leq N\leq 200,-1\leq a_i\leq1000,0\leq p_i\leq100\)。
正文:
这道题 \(K\leq2000\) 很搞人,但是我们发现其实 \(K\) 至多到 \(200\)。毕竟 \(a_i\) 最少是 \(-1\),就是最多加上 \(n\) 个 \(-1\) 才会对结果有影响。那么我们确定了 \(K\) 最终的范围:\(-200\leq K\leq200\)。
搞定 \(K\) 以后,我们设 \(f_{i,j,k}\) 表示前 \(i\) 个任务成功了 \(j\) 个当前的 \(K\) 为 \(k\) 的概率。
得到转移方程:
\[f_{i,j,k}=f_{i-1,j,k}\cdot(1-p_i)+f_{i-1,j,k-a_i}\cdot p_i
\]
很明显,\(f_{0,0,K}=1\)。
代码:
double f[N][N][N * 2];
int a[N];
double p[N], ans;
int n, l, K;
int main()
{
scanf ("%d%d%d", &n, &l, &K);
for (int i = 1; i <= n; i++)
scanf ("%lf", &p[i]), p[i] /= 100.0;
for (int i = 1; i <= n; i++)
scanf ("%d", &a[i]), a[i] = a[i] > 200? 200: a[i];
f[0][0][K + 200] = 1;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= i; j++)
for (int k = -200; k <= 200; k++)
f[i][j][k + 200] = j > 0? f[i - 1][j][k + 200] * (1.0 - p[i]) + f[i - 1][j - 1][k - a[i] + 200] * p[i]: f[i - 1][j][k + 200] * (1.0 - p[i]);
for (int i = l; i <= n; i++)
for (int k = 200; k <= 400; k++)
ans += f[n][i][k];
printf ("%.6lf", ans);
return 0;
}