来源:https://vjudge.net/problem/HDU-2955

不好理解的 01背包 

关键 :用成功逃走的概率当做价值,银行的总钱数当做背包容量

 

思路:题目中给定价值和被抓几率,但是被抓几率不可以用乘积来组合计算,举个例子,比如第一个银行3%被抓几率,第二个5%被抓几率,那么乘起来会变成0.15%,抢的越多,被抓几率却越小了,显然不对,因此要转换成不被抓几率,上述例子则变为第一家97%不被抓,第二家95%不被抓,乘起来就是92.15%,抢的越多,不被抓的几率越来越小即被抓几率越来越大,这样才是符合常理的。

 

那么背包体积应该是什么呢?先看最普通01背包,用数个cost来填充V,使得value之和尽量大,那么这题就应该是用数个money填充总money,使得不被抓几率尽量大。那转移方程就是dp[j]=max(dp[j],dp[j-w]*c),这里和01背包的区别就是从+改成了*。然后得到dp数组是0~V情况下的不被抓几率的最优(大)值,这根答案有什么关系?逆序枚举每一种情况,若此情况下的dp值即不被抓几率大于等于题目中所给的不被抓几率,那就输出,逆序着从大到小枚举保证了找到的一个解是最优解。

#include <iostream>
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
using namespace std;
double dp[10010];
int a[110];
double cost[110];
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        double k;
        int n,sum = 0;
        scanf("%lf %d",&k,&n);
        k = 1 - k;
        memset(dp,0,sizeof(dp)),memset(cost,0,sizeof(cost)),memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            scanf("%d %lf",&a[i],&cost[i]);
            cost[i] = 1.00 - cost[i];
            sum += a[i];
        }
        dp[0] = 1;
        for(int i=1;i<=n;i++)
        {
            for(int j=sum;j>=a[i];j--)
            {
                dp[j] = max(dp[j],dp[j-a[i]]*cost[i]);
            }
        }
        for(int i=sum;i>=0;i--)
        {
            if(dp[i] >= k)
            {
                printf("%d\n",i);
                break;
            }
        }
    }
    return 0;
}