守卫者的挑战

守卫者的挑战

您初始有一个容量为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;
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10948759.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值