这个博客只是为了记录自己做题的成长,希望要学习的小伙伴们还是不要看了,毕竟自己水平有限不要误导了你们……
1.思路
这道题输入一个p,是小偷总的被抓的概率,n是银行数量,接下来n行每行两个数,分别是银行有的资金数量和抢劫这个银行被抓的概率(每个银行的逃跑概率是独立的)。只要小偷被抓的概率低于总被抓概率就看做是可以逃跑,要我们输出在小偷可以逃跑的前提下最多可以抢劫的钱是多少。
首先,当然想到是动态规划了,但是在经典的01背包问题里,背包容量是整数,还可以作为数组的下标,这里的背包容量是概率,取到几位精度都不知道,所以要换一个角度,dp[i+1][j]来记录从前i个银行抢劫j元可以逃跑的概率,不存在这种情况就用0表示(以便后面的比较),但是不知道怎么我的代码总是错了,我就去上网搜了搜,总结了几点自己出错的原因。
2.解题误区
1.总的逃跑概率不是抢劫的每一家银行的逃跑概率的和!
诶,都怪自己概率论不好好学,现在傻了。比说总共有两家银行,第一家银行的被抓的概率是p1=0.05,第二家p2=0.06,如果两家都抢,逃跑的概率是(1-p1)*(1-p2),只要这个值大于1-p,这个小偷就可以逃跑。
2.初值怎么设置
首先,用二维数组dp[i+1][j]来思考,这个数组是从前i家银行抢劫j元的可以逃跑的概率,很显然,j=0,也就是啥都不抢的可以逃跑的概率当然是1了!所以dp[i][0]=1,那么其它的设为什么呢?一开始我想到由于这个转移动态方程是 dp[i+1][j]=max(dp[i][j],dp[i][j-m[i]]*(1-p[i])),初值设为0的话乘出来的概率不是0了吗?后面发现我根本就没有深入理解这个方程!
第一,假设有三家银行,分别的钱数是1 2 3,所以小偷最多可以抢劫三家银行,那么我的dp数组的j最大值也就是6;因为我们是从i=1开始的,i=1只有一种可能的方案啊,就是抢一元钱,那么只有dp[1][1]=max(dp[0][1],dp[0][1-1](1-p[1]),从这个式子看出来,其他的初值第最好要设为0以下的,因为我们用的是max函数!再往下一步看,i+1=2的时候,j为可行方案的也就只能取1(抢第一家不抢第二家),3(第一家第二家都抢),2(抢第二家不抢第一家),0(都不抢)。j=1的时候,dp[2][1]=dp[1][1],这个是在前一步算了的。j=2的时候,dp[2][2]=max(dp[1][2],dp[1][0](1-p[2]),显然,dp[1][2]我们设的就很小,自然不会取。如此,这个过程就很清晰了。
以下是代码:
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<cmath>
#include<queue>
#include<iomanip>
#include<cstdio>
#include<cctype>
#include<time.h>
#include<sstream>
#include<iostream>
#define mod 1000000007
#define maxn 10005
#define fre freopen("C:\\Users\\dell-c\\Desktop\\in.txt","r",stdin)
using namespace std;
int t,n;
float p;
float pi[105];
int money[105];
float dp[10005];
int main(){
scanf("%d",&t);
while(t--)
{
scanf("%f %d",&p,&n);
int total=0;
for(int i=0;i<n;i++)
{
scanf("%d %f",&money[i],&pi[i]);
total+=money[i];
}
memset(dp,0,sizeof dp);
dp[0]=1;
for(int i=0;i<n;i++){
for(int j=total;j>=money[i];j--)
{
dp[j]=max(dp[j],dp[j-money[i]]*(1-pi[i]));
}
}
for(int i=total;i>=0;i--){
if(dp[i]>(1-p))
{
printf("%d\n",i);
break;
}
}
}
return 0;
}