https://vjudge.net/problem/HDU-2955
题意概括
给出小偷偷各家银行被抓的概率和所获得的钱数,给出允许被抓的最小概率值p(挺怪的),试问在被抓概率不超过p时,所能偷到的最多钱数。
思路
先搬出大佬的思路,可供参考~~
https://blog.csdn.net/a1097304791/article/details/83586769
0/1背包这个不难想,但是很容易把概率当做背包容积,钱数当做dp值,其实有两个问题:
1.概率是小数。当然,你可以乘上10^n倍,就算解决了吧。
2.这样做并没有意识到几个银行之间的被抓概率之间的关系。
被抓的概率不是简单相乘或者相加。所以即使把概率变成了整数,也是没有用处的。
首先我们应该思考小偷偷了几家银行被抓的概率怎么求。
设偷各家银行被抓的概率为p1,p2,…pn;
被抓的概率应该是(从反面入手):
1-(1-p1)* (1-p2)*… * (1-pn)
这是乘法原理。
这题是一个0/1背包,但他也是一个概率dp.
所以干脆让dp[k]表示偷到的钱数为k时,不被抓的概率大小(处理简单),用0/1背包的方法扫过去,就可以得出结果了。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
const double eps=1e-6;
int n,sum,mon[105];
double dp[maxn],p,poss[105];
void ZeroOnePack(double b,int v)
{
int i;
for(i=sum;i>=v;i--)
{
if(dp[i-v]*b-dp[i]>=eps)
{
dp[i]=dp[i-v]*b;
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
double temp;
memset(dp,0,sizeof(dp));
scanf("%lf%d",&temp,&n);
p=1.0-temp;
sum=0;
int i;
for(i=1;i<=n;i++)
{
scanf("%d%lf",&mon[i],&poss[i]);
poss[i]=1-poss[i];
sum+=mon[i];
}
dp[0]=1.0;
for(i=1;i<=n;i++)
{
ZeroOnePack(poss[i],mon[i]);
}
for(i=sum;i>=0;i--)///从后往前扫描
{
if(dp[i]-p>=eps)
{
printf("%d\n",i);
break;
}
}
}
return 0;
}