这是一道关于小数的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
*/