【动态规划】守护者的挑战

【问题描述】

打开了黑魔法师Vani 的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi 的
监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图„„”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为K 的包包。擂台赛一共有N 项挑战,各项挑战依次进行。第i项挑战有一个属性ai ,如果 ai>=0,表示这次挑战成功后可以再获得一个容量为ai的包包;如果ai=-1,则表示这次挑战成功后可以得到一个大小为1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把获得的所有的地图残片都带走(没有得到的不用考虑,只需要完成所有N 项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功L 次才能离开擂台。队员们一筹莫展之时,善良的守卫者Nizem 帮忙预估出了每项挑战成功的概率,其中第i 项挑战成功的概率为pi。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。

【问题分析】
动态规划求解 考场上首先想到的就是F[i][j][k][l]表示前i个挑战之中获胜j次,当前地图有k个,背包容量为l的概率。但是根据此题数据范围n<=200,会TLE,所以想了一下,可以将k与l压维,新的状态为F[i][j][k]表示前i个挑战之中获胜j次,背包还可以装k个地图的概率,此时-200<=k<=200,所以就巧妙地合并了k l俩维。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N=210;
int n,k,l,zero=210; int a[N];
double p[N],ans; double F[2][N][N*2];
int readin()
{
    int x=0,f=1; char ch=getchar();
    while(ch>'9'||ch<'0') {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int mymax(int a,int b)
{
    return a>b?a:b;
}
void read()
{
    int i;
    n=readin(); l=readin(); k=readin();
    k=min(k,n);
    for (i=1;i<=n;i++)
    {
        p[i]=readin();
        p[i]/=100.00;
    }
    for (i=1;i<=n;i++)
        a[i]=readin();
    return;
}
void dp()
{
    int i,j,x;  int now=1,last=0;
    F[1][1][mymax(n,zero+k+a[1])]=p[1];
    F[1][0][k+zero]=1-p[1];
    for (i=2;i<=n;i++)
    {
        swap(now,last);
        memset(F[now],0,sizeof(double)*N*N*2);
        for (x=zero-n;x<=zero+200;x++)//当j==0时可知之前无胜利 特殊的转移
            F[now][0][x]+=(1-p[i])*F[last][0][x];
        for (j=1;j<=n;j++)
            for (x=zero-n;x<=zero+200;x++)
            {
                F[now][j][x]+=p[i]*F[last][j-1][x-a[i]];
                F[now][j][x]+=(1-p[i])*F[last][j][x];
            }
    }
    for (i=zero;i<=zero+200;i++)
        for (j=l;j<=n;j++)
        ans+=F[now][j][i];
    printf("%.6lf\n",ans);
    return;
}
int main()
{
    freopen("guard.in","r",stdin);
    freopen("guard.out","w",stdout);
    read();
    dp();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值