题意:
好多个银行,他们很有钱。有个人想去抢劫。告诉你银行的钱和抢这个银行被抓的概率。银行被你抢一次就会没钱。现在需要你满足的条件是被抓的概率小于他给的stand,问,最多能得到多少钱。
做题过程:
这是一道很有现实主义色彩,很实用的一道题。总共就n个银行,他们钱数就是背包的容量,每个银行只抢一次,由此看出是个01背包,背包出的价值(概率)最大。开始我还以为要dp出个最小呢。现在想想,应该是在获得钱数尽可能多的情况下求最大逃脱概率才对。
状态转移方程:d[money] = max( d[money - m[i]] * (1 - p[i] ) ). d[money]就是获得money的最大逃脱概率。
其中,为什么不能求最小被抓概率呢?因为求不出来。最小被抓概率= 其中一个被抓 || 其中两个被抓。。。。|| 全被抓了。所以,高数告诉我们,从反面考虑。
其次,这里一个优化是用sum记录前几个的钱之和(体积和),因为即使前面几个都放进去,也不会超过那个容量的。
最后,我表示疑惑,传说中的01背包不是要从后往前for的吗?我从后往前给wa了。难道,从后往前的意思是代码中的第二个for。我咋记得重要一个for就行了,为什么这题是两个for呢。 第一道01背包题!WA了几次。。。
/* Pro: 0 Sol: date: */ #include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <cmath> #include <queue> #include <set> #include <vector> #define maxn 120 using namespace std; int m[maxn],t,n,sum[maxn]; double p[maxn],stand,d[maxn * maxn]; int main(){ scanf("%d",&t); while(t --){ scanf("%lf%d",&stand,&n); sum[0] = 0; for(int i = 1; i <= n;i ++){ scanf("%d%lf",&m[i],&p[i]);//被抓的概率 sum[i] = sum[i - 1] + m[i]; } memset( d, 0,sizeof(d)); d[0] = 1;//不抢钱,被抓的概率为1 for(int i = 1; i <= n; i ++){ for(int j = sum[i]; j >= m[i]; j --){ d[j] = max(d[j],d[j - m[i]] * (1 - p[i]));//逃脱的概率,取最大的 } } for(int i = sum[n]; i >= 0; i --)//必须是大于等于0,而不是1,可以不抢钱 if(d[i] > 1 - stand){ printf("%d\n",i) ; break; } } return 0; }