01背包 HDU - 2955

hdu2955

一 

先复习一下01背包这个经典模型。所谓01背包,就是在一些价值、重量不等的物品中,挑选一部分放入一个有限容量的背包中,求背包中的物品可以达到的最大价值和。这个问题中每个物品的状态就是选择或者不选择,所以称“01”背包。

看这篇博客复习起来的,觉得写得很好,收藏一下

https://blog.csdn.net/AC__GO/article/details/76918559

题意是一个小偷,考虑在n家银行中挑选一些下手。第i家银行下手时,可以偷得的钱是mi百万,而被抓住的概率是pi,pi是浮点型小数。现在求被抓概率小于P时,可以偷得的最大钱数M。

刚读完题很顺畅地得到一个错误思路:典型的01背包问题。有n个物品,第i个的重量是pi,价值是mi,背包的承载是P,求最大价值。

这个思路有两个大问题。其一,pi是浮点型小数,01背包中处理重量的方法是作为下标来枚举,p是不能够枚举的。其二,概率进行的是乘法而非加法运算,最终的被抓概率不等于所选被抓概率的和,它直接计算比较繁琐。

为了解决这两个问题:我们把不方便枚举的概率看做物品的价值,而把对每家银行下手得到的金钱当做是重量。更直观地说,这个问题可以理解成,我们要承担一定数量的赃款,就要背负相应逃生几率的减少作为价值。初始化的时候,f[0]代表不对银行动手,承担0赃款,逃生几率就是1,而f[1 --> max]代表承担一定的赃款,但此时没有对银行动手,这个几率是没有意义,不存在的,我把它设置成负无穷,也就无法根据这些数值往后递推了。也就是这其实是一个“装满背包”的问题,我们的赃款一定满足下手银行可获得金钱的和,不然没有意义,同时也不能作为答案输出。

通过01背包的操作,注意这里递归条件是f[j]=max{f[j],f[j-m[i]]*p[i]},获得同样的赃款,选择逃生概率更大的下手方式。

最后从大到小扫描f[]数组,找到那一个逃生概率大于(1-p)的,输出赃款,也就是下标,即可。这里不可能得到的赃款数量对应的值一路更新过来都是负数,不会满足条件。

#include<bits/stdc++.h>
using namespace std;

double f[10010];//抢一定的钱数下最大的逃跑概率

int main()
{
    double P,p[110];//p用来存储逃跑概率

    int m[110],n;
    int t;
    cin>>t;
    while(t--)
    {
        scanf("%lf%d",&P,&n);
        int N=0;
        P=1-P;
        for(int i=1;i<=n;i++)
        {
            double tmp;
            scanf("%d%lf",&m[i],&tmp);
            N+=m[i];
            p[i]=1-tmp;
        }
        memset(f,-0x3f,sizeof(f));
        f[0]=1;
        for(int i=1;i<=n;i++)
            for(int j=N;j>=m[i];j--)
            {
                f[j]=max(f[j],f[j-m[i]]*p[i]);
            }
        int ans;
        for(int i=N;i>=0;i--)
            if(f[i]>=P) {ans=i;break;}
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xuzonghao/article/details/82667745
今日推荐