守卫挑战
题目链接:ybt金牌导航1-1-9
题目大意
有一些任务,每个任务有一定的几率可以成功。
成功会让一个初始为 K 的数加一个值。(不同的任务减去的值不同,会有 -1 和正整数)
然后要你在保证至少成功 L 次的情况下,最后这个值是非负数的概率。
思路
这道题的数据较小,考虑直接普通的 dp 概率。
然后虽然 K K K 可以到 2000 2000 2000,但是可以当做 200 200 200 来看。(反正两个都不会爆空间、影响结果,这样还能把 K K K 变成 200 200 200)也就是说,当任何一个时候 K K K 值大于 200 200 200 的时候,你都可以把它变成 200 200 200。(当然,负就是到 − 200 -200 −200)
那我们很容易设 f i , j , k f_{i,j,k} fi,j,k 为前 i i i 个任务中成功了 j j j 个,现在 K K K 值是 k k k 的概率。
那得到转移也很容易想到:
{ f i , j , k + a i = f i − 1 , j , k + a i × ( 1.0 − p i ) ( j = 0 ) f i , j , k + a i = f i − 1 , j , k + a i × ( 1.0 − p i ) + f i − 1 , j − 1 , k × p i ( j > 0 ) \left\{\begin{matrix} f_{i,j,k+a_i}=f_{i-1,j,k+a_i}\times (1.0 - p_i)&(j=0)\\ f_{i,j,k+a_i}=f_{i-1,j,k+a_i}\times (1.0 - p_i)+f_{i-1,j-1,k}\times p_i&(j>0) \end{matrix}\right. {
fi,j,k+ai=fi−1,j,k+ai×(1.0−pi)fi,j,k+ai=fi−1,j,k+ai×(1.0−pi)+fi−1,j−1,k×pi(j=0)(j>0)
当然,要记得 k k k 值要与 200 200 200 取最小值。
而且你会发现 k k k 的范围是 − 200 ∼ 200 -200\sim200 −200∼200,那就要把所有的 k k k 都加 200 200 200(都变成非负的,才可以放在数组里),而且去最小值的时候就是跟 400 400 400 比了。
至于初始化,根据上面要把 k k k 加 200 200 200,原来应该是 f 0 , 0 , K = 1 f_{0,0,K}=1 f0,0,K=1 的就变成了 f 0 , 0 , K + 200 = 1 f_{0,0,K+200} = 1 f0,0,K+200=1。
(一开始 k k k 是 K K K 是因为一开始的 K K K 就是这个值)
然后 dp 完之后,我们看看怎么统计答案。
很容易想到,我们枚举最后任务成功的数量 i i i,范围是 L ∼ n L\sim n L∼n,然后在枚举最后 K K K 的值 j j j,范围是 0 ∼ 200 ( + 200 ) 0\sim200(+200) 0∼200(+200),然后所有的 f n , i , j f_{n,i,j} fn,i,j 相加就是我们要的答案了。
代码
#include<cstdio>
#include<iostream>
#define bp_add 200
using namespace std;
int n, l, K, a[201];
double p[201], f[201][201][402], ans;
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]);
}
f[0][0][K + bp_add] = 1.0;
for (int i = 1; i <= n; i++)
for (int j = 0; j <= i; j++)
for (int k = -200; k <= 200; k++) {
f[i][j][min(400, k + bp_add + a[i])] = f[i - 1][j][min(400, k + bp_add + a[i])] * (1.0 - p[i]);
if (j) {
f[i][j][min(400, k + bp_add + a[i])] += f[i - 1][j - 1][min(400, k + bp_add)] * p[i];
}
}
for (int i = l; i <= n; i++)
for (int j = bp_add + 0; j <= 200 + bp_add; j++)
ans += f[n][i][j];
printf("%.6lf", ans);
return 0;
}