codevs 3269 混合背包

题目描述 Description

  背包体积为V ,给出N个物品,每个物品占用体积为Vi,价值为Wi,每个物品要么至多取1件,要么至多取mi件(mi > 1) , 要么数量无限 , 在所装物品总体积不超过V的前提下所装物品的价值的和的最大值是多少?

输入描述 Input Description

  第一行两个数N,V,下面N行每行三个数Vi,Wi,Mi表示每个物品的体积,价值与数量,Mi=1表示至多取一件,Mi>1表示至多取Mi件,Mi=-1表示数量无限

输出描述 Output Description

  1个数Ans表示所装物品价值的最大值

样例输入 Sample Input

  2 10

  3 7 2

  2 4 -1

样例输出 Sample Output

  22

数据范围及提示 Data Size & Hint

  对于100%的数据,V <= 200000 , N <= 200

 题解:01+多重+完全背包。多重背包必须加二分制优化。不然全部超时。

          二分制优化:将第i件物品分为若干件物品,其中每件物品都有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数,是这些系数分别为1,2,4,...2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。而这些数字可以组合成1~n[i]内的所有数字。

                   例如12,可将其分为1,2,4,5.

#include<cstdio>
#include<iostream>
#define N 210
#define V 200100
using namespace std;
int n,v;
int vi[N],wi[N],mi[N],f[V]={0};
int main()
{
    scanf("%d%d",&n,&v);
    for (int i=1;i<=n;i++) scanf("%d%d%d",&vi[i],&wi[i],&mi[i]);
    for (int i=1;i<=n;i++)
      {
           if (mi[i]==-1)//完全背包 
             for (int j=vi[i];j<=v;j++) f[j]=max(f[j],f[j-vi[i]]+wi[i]);
           else  
             {
                for (int k=1;k<=mi[i];k++)//01和多重背包 
                   for (int j=v;j>=vi[i];j--)
                      f[j]=max(f[j],f[j-vi[i]]+wi[i]);    
             }
           
      }
    cout<<f[v]<<endl;
    return 0;
}
未加二分制的超时代码
#include<cstdio>
#include<iostream>
#define N 210
#define V 200100
using namespace std;
int n,v;
int vi[N],wi[N],mi[N],f[V]={0};
int main()
{
    scanf("%d%d",&n,&v);
    for (int i=1;i<=n;i++) scanf("%d%d%d",&vi[i],&wi[i],&mi[i]);
    for (int i=1;i<=n;i++)
      {
           if (mi[i]==-1)
             for (int j=vi[i];j<=v;j++) f[j]=max(f[j],f[j-vi[i]]+wi[i]);
           else  
             {
                   int x=mi[i];
                   for (int k=1;k<=x;k<<=1)//二分制优化 
                   
                      {
                           for (int j=v;j>=vi[i]*k;j--)
                             f[j]=max(f[j],f[j-vi[i]*k]+wi[i]*k); 
                           x-=k;   
                  }
              if (x)
                for (int j=v;j>=vi[i]*x;j--)
                  f[j]=max(f[j],f[j-vi[i]*x]+wi[i]*x);
             }
      }
    cout<<f[v]<<endl;
    return 0;
}
加了二分制后的满分代码
原文地址:https://www.cnblogs.com/sjymj/p/6005648.html