守卫者的挑战
守卫者的挑战
题目描述
打开了黑魔法师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%
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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; }