这是一道关于小数的01背包问题,题意代码注释中有,如果按着题的思路来写,会发现那个概率是小数,在转移方程里没法实现,所以我们需要换个方向思考了。我们可以按成功逃跑的概率来算,每个w数组里存成功逃跑的概率,然后让总价值作为背包容量,然后在dp中用价值去存逃跑的概率,最后从价值最大(背包容量)到小遍历(因为价值越大逃跑成功率越低),直到第一次出现逃跑成功的概率小于等于题目给的逃跑成功概率,输出此使的价值即可。我发现我解释的也不是很清楚,所以结合代码注释看一下吧,感觉不是很好理解。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int MAXN = 10005; //数组尽量稍大点
double dp[MAXN],w[MAXN];
int val[MAXN];
int T,n;
double Max;
int main()
{
scanf("%d",&T);
while(T--){
int sum = 0;
scanf("%lf%d",&Max,&n);
Max = 1-Max; // 不被抓的概率
for(int i=0;i<n;i++){
scanf("%d%lf",&val[i],&w[i]);
w[i] = 1 - w[i]; // 转换为成功的概率
sum += val[i]; // 记录所有银行价值总和
}
memset(dp,0,sizeof(dp)); // 初始化为0 表示逃跑成功率为0
dp[0]=1; // 当什么也没抢到的时候成功率设为1
for(int i=0;i<n;i++){ // 遍历银行
for(int j = sum;j>=val[i];j--){ // 01背包过程
dp[j] = max(dp[j],dp[j-val[i]]*w[i]); // 把每个逃跑成功的概率放入dp存起来,所以需要乘w[i]
}
}
for(int i=sum;i>=0;i--){ // 从总和(背包容量)开始往后遍历
if(dp[i]>Max){ // 找到第一个逃跑成功率大于要求的成功率即可
printf("%d\n",i); // 输出其抢到的最大价值
break;
}
}
}
return 0;
}
/*
[来源] HDU 2955
[题目]
Robberies
[题意]
Royi昂要抢劫银行,每家银行都有一定的金额和被抓到的概率,题中给了Roy被抓的最大概率,问在不被抓的情况下,他能抢的
金额的最大值。可以反向思考,根据不被抓的概率来算。把逃走的概率当作价值,银行的总钱数当作背包容量。
[输入]
3
0.04 3
1 0.02
2 0.03
3 0.05
0.06 3
2 0.03
2 0.03
3 0.05
0.10 3
1 0.03
2 0.02
3 0.05
[输出]
2
4
6
*/