多重背包二进制优化(wzk吃小鸡腿)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangjingyanzjyer/article/details/79092972

问题 B: WZK吃小鸡腿(chicken)

时间限制: 1 Sec   内存限制: 128 MB
提交: 53   解决: 23
[ 提交][ 状态][ 讨论版]

题目描述

As is known to all,WZK很能吃小鸡腿,但他的胃毕竟有一个最大容纳值c,否则胃中小鸡腿的巨大引力场和lōng场叠加后会有很可怕的效果。在CZYZ的食堂一共有n种小鸡腿有卖,每种小鸡腿都有有限的个数、重量和让WZK感到的满意度。WZK想在肚子不被撑爆的前提下,取得最大的满意度值。然后lōng场就得到了大大的power up。

输入

第一行:两个正整数n,c。 接着n行,每行三个正整数Ai,Mi,Wi,分别表示每种小鸡腿的个数、重量和满意度。

输出

一个正整数,表示最大的满意度。

样例输入

3 102 1 31 5 83 3 5

样例输出

19

提示

20%的数据ΣAi<=25

另外30%的数据c<=1000, ΣAi<=10000

对于100%的数据c<=10000,n<=100,Ai<=1000


这道题的算法是很明显的,多重背包。但是,普通的多重背包是三重循环的,所以肯定是要超时的(然而因为数据太水,被我三重循环水过了)。所以,这里要用到一个多重背包问题的常见优化,二进制优化。我们可以发现,多重背包的主要时间复杂度多在那一个多出来的k循环,也就是那个物品个数的循环。如果能把这个循环进行优化甚至去掉,那就可以节省大量的时间。而又因为多重背包的原型就是01背包,所以想到将其转换回01背包。那么就要将这些物品进行处理。原来的多重背包,那个k循环就相当于把有k个的一种物品分成了k个物品,但是这样过于浪费时间。所以在将物品进行重新处理的时候,我们 可以用到二进制的思想。首先,举个例子:1,2,4,8个物品可以组成1-15任何个数的物品。这一点就是二进制优化的核心内容。将物品的个数以二进制的方式进行处理,那么就可以在减少分的物品个数(这样子分肯定是要比k要小的)的同时能够达到1-原来个数之间的任意数。所以,我们在读入的时候可以对数据一一进行重新处理。如:一种物品有8个 每个物品重量为5  价值为10,那么进行处理之后就变成个数为1,2,4,1的四个物品,这四个物品的重量分别为5,10,20,5,价值分别为10,20,40,10。这样就让这一种物品变成了新的四个物品。将所有数据都进行处理完之后,只要再按01背包来一个双重循环即可解决。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
int tot,n,c;
int d[1000],a[11000],m[11000],w[11000],m1[11000],w1[11000],p[11000];
void put(int a0,int m0,int w0)//对数据进行重新处理
{
    int i=1;
    while (a0>d[i])
    {
        a0-=d[i];
        tot=tot+1;
        m1[tot]=d[i]*m0;
        w1[tot]=d[i]*w0;
        i=i+1;
    }
    tot=tot+1;
    m1[tot]=a0*m0;
    w1[tot]=a0*w0;
}
using namespace std;
int main()
{
    tot=0;
    scanf("%d%d",&n,&c);
    d[1]=1;
    for (int i=2;i<=15;i++)//产生二进制数
     d[i]=d[i-1]*2;
    for (int i=1;i<=n;i++)
     {
        scanf("%d%d%d",&a[i],&m[i],&w[i]);
        put(a[i],m[i],w[i]);
     }
    for (int i=1;i<=tot;i++)//01背包的dp,注意这里i要到tot不是n,因为数据处理过了
     for (int j=c;j>=m1[i];j--)
        p[j]=max(p[j],p[j-m1[i]]+w1[i]);
    printf("%d",p[c]);
      
}

猜你喜欢

转载自blog.csdn.net/zhangjingyanzjyer/article/details/79092972