守卫者的挑战
您初始有一个容量为k的包,现在有n项挑战依次进行,第i项挑战成功的概率为\(p_i\%\),每次挑战成功会得到一个\(a_i\),\(a_i=-1\)表示得到一个地图碎片,否则表示得到一个容量为\(a_i\)的包,胜利的条件是至少成功l次,且得到的背包恰好能容纳所有的地图碎片(一个地图碎片占据容量1),问胜利的概率,\(0<=k<=2000,0<=n<=200,-1<=ai<=1000,0<=L<=N,0<=pi<=100\)。
解
不难想到要表现这是那一场挑战,除此之外,为了判断胜利,自然也要表现地图碎片个数与背包容量,但这两个没必要分开来表示,统一表示为剩余背包容量(当然可以为负数,所以需要把0定义为一个比较大的数,防止越界),而还有一个胜利判断是胜利次数,于是我们自然想到设\(f[i][j][k]\)表示到了第i场挑战,剩余背包容量为j,挑战成功l次的概率,注意到实际上背包容量很大,但是地图碎片很少,意味着,我们需要的背包容量,不会超过n,于是我们可以考虑压范围,而压范围,自然顺转移比较舒服,于是有
第i+1场失败:\(f[i+1][j][k]+=f[i][j][k]\)
第i+1场成功:\(f[i+1][j+a_{a+1}][k+1]+=f[i][j][k]\)
边界:\(f[0][zero][0]=1,zero\)为您自行定义的0点
答案:\(\sum f[n][i][j]\),i枚举大于0的部分,j枚举大于l的部分
因为我们压了范围,于是当转移的变量超过范围,以范围最大量代替转移的变量。
参考代码:
#include <iostream>
#include <cstdio>
#define il inline
#define ri register
using namespace std;
int a[201];
double p[201],dp[201][201][401];
int main(){
int n,l,k;
scanf("%d%d%d",&n,&l,&k);
for(int i(1);i<=n;p[i]/=100,++i)scanf("%lf",&p[i]);
for(int i(1);i<=n;++i)scanf("%d",&a[i]);
dp[0][0][200+k]=1;
for(int i(0),j;i<n;++i)
for(j=0;j<=l&&j<=i;++j)
for(k=0;k<=400;++k){
dp[i+1][j][k]+=dp[i][j][k]*(1-p[i+1]);
int j_(j+1),k_(k+a[i+1]);
if(j_>l)j_=l;if(k_>400)k_=400;
if(k_<0)k_=0;dp[i+1][j_][k_]+=dp[i][j][k]*p[i+1];
}double ans(0);
for(k=200;k<=400;++k)ans+=dp[n][l][k];
printf("%.6lf",ans);
return 0;
}