题目
解法
比较容易想出一个 (mathtt{dp}):令 (dp_{i,j,k}) 为前 (i) 天赢了 (j) 场,剩余容量为 (k) 的概率。转移方程就是:
[dp_{i,j,k}=dp_{i-1,j,k}cdot (1-p_i)+dp_{i-1,j-1,k-a_i}cdot p_i
]
看似 (k) 这一维比较大,但是实际上我们可以将其限制在 (n) 的范围,也即 (200) 以内。因为只要容量到达了 (200) 就一定够减了,而且转移方程的系数与 (k) 无关。相当于最后统计 (sum_{i=l}^nsum_{j=0}^{200}{dp_{n,i,j}})。这样就是 (mathcal O(n^3))。
但是实际上这样转移是有一定问题的。我们可以先获得后面的容积,然后再得到前面的物品。不妨将容积扩大到 ([0,400]),最后取剩余容量在 ([200,400]) 的 (dp) 值即可。
代码
#include <cstdio>
#include <iostream>
using namespace std;
int n, l, k, val[205];
double dp[205][205][405], p[205], ans;
int read() {
int x = 0, f = 1; char s;
while((s = getchar()) < '0' || s > '9') if(s == '-') f = -1;
while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
return x * f;
}
int main() {
n = read(), l = read(), k = read();
for(int i = 1; i <= n; ++ i) scanf("%lf", &p[i]), p[i] /= 100;
for(int i = 1; i <= n; ++ i) val[i] = read();
dp[0][0][k + 200] = 1;
for(int i = 0; i < n; ++ i)
for(int j = 0; j <= i; ++ j)
for(int v = 0; v <= 400; ++ v) {
int s = min(400, v + val[i + 1]);
dp[i + 1][j][v] += dp[i][j][v] * (1.0 - p[i + 1]);
if(s >= 0) dp[i + 1][j + 1][s] += dp[i][j][v] * p[i + 1];
}
for(int i = l; i <= n; ++ i)
for(int j = 200; j <= 400; ++ j)
ans += dp[n][i][j];
printf("%.10f
", ans);
return 0;
}