守卫者的挑战

守卫者的挑战

题目描述

打开了黑魔法师Vani的大门,队员们在迷宫般的路上漫无目的地搜寻着关押applepi的监狱的所在地。突然,眼前一道亮光闪过。“我,Nizem,是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图……”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为K的包包。

擂台赛一共有N项挑战,各项挑战依次进行。第i项挑战有一个属性ai,如果ai>=0,表示这次挑战成功后可以再获得一个容量为ai的包包;如果ai=-1,则表示这次挑战成功后可以得到一个大小为1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把 【获得的所有的】地图残片都带走(没有得到的不用考虑,只需要完成所有N项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功L次才能离开擂台。

队员们一筹莫展之时,善良的守卫者Nizem帮忙预估出了每项挑战成功的概率,其中第i项挑战成功的概率为pi%。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。

输入格式

第一行三个整数N,L,K。

第二行N个实数,第i个实数pi表示第i项挑战成功的百分比。

第三行N个整数,第i个整数ai表示第i项挑战的属性值.

输出格式

一个整数,表示所求概率,四舍五入保留6 位小数。

样例

样例输入1

3 1 0
10 20 30
-1 -1 2

样例输出1

0.300000

样例输入2

5 1 2
36 44 13 83 63
-1 2 -1 2 1

样例输出2

0.980387

数据范围与提示

若第三项挑战成功,如果前两场中某场胜利,队员们就有空间来容纳得到的地图残片,如果挑战失败,根本就没有获得地图残片,不用考虑是否能装下;

若第三项挑战失败,如果前两场有胜利,没有包来装地图残片,如果前两场都失败,不满足至少挑战成功次()的要求。因此所求概率就是第三场挑战获胜的概率。

对于 100% 的数据,保证0<=K<=2000,0<=N<=200,-1<=ai<=1000,0<=L<=N,0<=pi<=100。

CODE

MLE 90%

 

 1 #include <bits/stdc++.h>
 2 
 3 using namespace std;
 4 
 5 typedef long long ll;
 6 typedef unsigned long long ull;
 7 const int maxn = 202;
 8 const int maxm = 9e4 + 10;
 9 
10 int n, L, k, c[maxn];
11 double p[maxn], dp[3][maxn][maxn*3+10];
12 
13 inline int read()
14 {
15     int x = 0, f = 1;
16     char ch = getchar();
17     while(ch > '9' || ch < '0')
18     {
19         if(ch == '-')
20         {
21             f = -1;
22         }
23         ch = getchar();
24     }
25     while(ch >= '0' && ch <= '9')
26     {
27         x = (x << 1) + (x << 3) + (ch^48);
28         ch = getchar();
29     }
30     return x * f;
31 }
32 
33 int main()
34 {
35     n = read(); L = read(); k = read();
36     for(int i=1; i<=n; i++)
37     {
38         p[i] = read(); p[i] /= 100.0;
39     }
40     for(int i=1; i<=n; i++)
41     {
42         c[i] = read();
43     }
44     /*
45     题解说为了使数组下标不为负,要确定一个“绝对零点”,或者还可以叫“起点”
46     qes1:为什么绝对零点不是n
47     n的意义是背包为空,但是绝对零点是指背包大小的初始值,0的意义是背包的下限
48     pre+n是上限
49     那么为什么要从k和n里选一个小的呢,因为k-n多余的部分永远都用不着,白占不少空间
50     背包大小无限和大小围n等效
51     */
52     int pre = n + min(n, k);
53     //dp[i][j][k]:前i次挑战获胜j次,背包容量还剩k的概率
54     dp[0][0][pre] = 1.0;
55 
56     for(int i=1; i<=n; i++)//第i次挑战
57     {
58         for(int j=0; j<n; j++)//前i-1次已经获胜了j次,为什么要循环到n
59         {
60             for(int k=0; k<=pre+n; k++)//前i-1的背包容量
61             {
62                 dp[i%2][j][k] += dp[(i-1)%2][j][k] * (1.0 - p[i]);
63                 //输:获胜次数和背包容量都不变
64                 if(c[i] == -1)//获胜后拿到了碎片
65                 {
66                     dp[i%2][j+1][k-1] += dp[(i-1)%2][j][k] * p[i];
67                 }
68                 else
69                 {
70                     /*
71                     qes2:为什么会有超过上限的可能?
72                     上限是根据可能的碎片数确定的,当然可以过线,过线的部分同样无效
73                     */
74                     dp[i%2][j+1][min(pre+n, k+c[i])] += dp[(i-1)%2][j][k] * p[i];
75                 }
76             }
77         }
78     }
79     double ans = 0;
80     for(int i=L; i<=n; i++)
81     {
82         for(int j=n; j<=pre+n; j++)//从n到Max and Max以上是合法的
83         {
84             ans += dp[n%2][i][j];
85         }
86     }
87     printf("%.6lf", ans);
88 
89     return 0;
90 }
View Code

 

AC

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
const int maxn = 202;
const int maxm = 9e4 + 10;

int n, L, k;
double p[maxn], f[maxn][maxn][maxn<<1|1], ans;

inline int read()
{
    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 << 1) + (x << 3) + (ch^48);
        ch = getchar();
    }
    return x * f;
}

int main()
{
    n = read(); L = read(); k = read();
    for(int i=1; i<=n; i++)
    {
        p[i] = read(); p[i] /= 100.0;
    }
    if(k > n) k = n;
    double ans = 0;
    f[0][0][n+k] = 1;
    //以上部分没变化
    for(int i=1; i<=n; i++)
    {
        int x = read();//边读入边处理会省一些空间
        for(int j=0; j<i; j++)
        {
            //第三维表示剩余容量,只要大于n就必然成功,上一篇在pre的基础上再加n就没有必要了
            for(int k=0; k<=n+n; k++)//从0到2n之间波动会省一些空间
            {
                //if(k + x >= 0)//为什么?中途不应该可以允许不够用的情况吗
                //好吧我尝试了一下把if去掉似乎并不影响结果,那我就不加他了
                {
                    //-1是背包空间少了一个,恰好对应,可以不用分类直接用
                    f[i][j+1][min(n+n,k+x)] += p[i] * f[i-1][j][k];
                    f[i][j][k] += (1-p[i]) * f[i-1][j][k];
                }
            }
        }
    }
    for(int j=L; j<=n; j++)
    {
        for(int k=n; k<=n+n; k++)
        {
            ans += f[n][j][k];
        }
    }
    printf("%.6lf", ans);

    return 0;
}

 

 

 

posted @ 2022-05-16 15:29  Catherine_leah  阅读(31)  评论(0编辑  收藏  举报
/* */