多重背包题集(HD2844,HD2191,HD1059)

来源:互联网 发布:2015年进出口数据统计 编辑:程序博客网 时间:2024/06/06 06:55

题目描述:

有N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

基本算法:

这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可,因为对于第i种物品有n[i]+1种策略:取0件,取1件……取 n[i]件。令f[i][v]表示前i种物品恰放入一个容量为v的背包的最大权值,则:f[i][v]=max{f[i-1][v-k*c[i]]+ k*w[i]|0<=k<=n[i]}。复杂度是O(V*∑n[i])。

转化为01背包问题
另一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]件01背包中的物品,则得到了物品数为∑n[i]的01背包问题,直接求解,复杂度仍然是O(V*∑n[i])。但是我们期望将它转化为01背包问题之后能够像完全背包一样降低复杂度。仍然考虑二进制的思想,我们考虑把第i种物品换成若干件物品,使得原问题中第i种物品可取的每种策略——取0..n[i]件——均能等价于取若干件代换以后的物品。另外,取超过n[i]件的策略必不能出现。
方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]为13,就将这种物品分成系数分别为1,2,4,6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。

但是,有时候单纯只转化为01背包问题,还是会超时,这时我们可以先用完全背包,在使用二进制转化后的01背包。先用完全背包做的前提是:某一种物品的总体积大于容器的体积,那么这时就相当于物品可以无限的取用,因为容器体积满了时,物品总是没有用光的。

看具体题目:

1、HD2844

题意:给出硬币的价值以及该价值硬币的数量还有m,计算在1到m之间的用给定的硬币可以拼凑出的钱的数目。在这个题中,硬币的价值看做物品的体积。

代码:

#include <iostream>#include<memory.h>using namespace std;int n,m,A[101],C[101],vol[101],V[100010];int main(){    int f,count;    while(cin>>n>>m&&n!=0&&m!=0)    {       count=0;      for(int i=0;i<n;i++)          cin>>A[i];      for(int i=0;i<n;i++)          cin>>C[i];      memset(V,0,sizeof(V));      for(int i=0;i<n;i++)      {         if(A[i]*C[i]>m)//用完全背包做         {           for(int j=A[i];j<=m;j++)             if(V[j]<V[j-A[i]]+A[i])                V[j]=V[j-A[i]]+A[i];         }         else        {            f=1;            int temp;          while(C[i]>f)          {            temp=A[i]*f;            for(int j=m;j>=temp;j--)                if(V[j]<V[j-temp]+temp)                V[j]=V[j-temp]+temp;            C[i]-=f;            f<<=1;          }          temp=A[i]*C[i];          for(int j=m;j>=temp;j--)            if(V[j]<V[j-temp]+temp)             V[j]=V[j-temp]+temp;        }      }      for(int i=1;i<=m;i++)        if(V[i]==i)            count++;      cout<<count<<endl;    }}

2、HD2191

这个题中,大米价格以及经费的金额看作是容器的体积,而大米的重量看作是物品的价值,根据题意是要求所能得到的大米的最大的重量。

代码:

#include <iostream>#include<memory.h>using namespace std;struct Rice{    int p,h;};Rice r[501];int M[20010];int main(){    int C,n,m,a,b,c,f;    cin>>C;    for(int i=0;i<C;i++)    {        cin>>n>>m;        memset(M,0,sizeof(M));        for(int j=0;j<m;j++)        {            cin>>a>>b>>c;            if(a*c>n)//用完全背包            {                for(int k=a;k<=n;k++)                    if(M[k]<M[k-a]+b)                    M[k]=M[k-a]+b;            }            else{            f=1;            while(c>f)            {                for(int k=n;k>=a*f;k--)                    if(M[k]<M[k-a*f]+b*f)                     M[k]=M[k-a*f]+b*f;                c-=f;                f<<=1;            }           for(int k=n;k>=a*c;k--)            if(M[k]<M[k-a*c]+b*c)             M[k]=M[k-a*c]+b*c;            }        }        cout<<M[n]<<endl;    }}

3、HD1059

题意:给出1-6每个数字的个数,然后给出的数字能否被分成两部分,使这两部分的和相等。这个题目中数字的价值可看作是背包的体积。

#include <iostream>#include<memory.h>using namespace std;int num[7],V[430000],a=0,vol[100];int main(){   int sum,f;   bool judge;   while(true)   {        a++;        judge=false;        sum=0;       for(int i=1;i<7;i++)      {       cin>>num[i];       if(num[i]!=0)       {           sum+=i*num[i];           judge=true;       }      }       if(!judge) break;        cout<<"Collection #"<<a<<":\n";       if(sum%2){            cout<<"Can't be divided.\n\n";            continue;       }        sum/=2;        memset(V,0,sizeof(V));       for(int i=1;i<7;i++)       {           if(i*num[i]>sum)//完全背包           {               for(int j=i;j<=sum;j++)                if(V[j]<V[j-i]+i)                 V[j]=V[j-i]+i;           }           else{           f=1;           while(num[i]>f)           {              for(int j=sum;j>=i*f;j--)                if(V[j]<V[j-i*f]+i*f)                  V[j]=V[j-i*f]+i*f;              num[i]-=f;              f<<=1;           }           for(int j=sum;j>=num[i]*i;j--)            if(V[j]<V[j-num[i]*i]+num[i]*i)              V[j]=V[j-num[i]*i]+num[i]*i;           }       }      if(V[sum]!=sum)        cout<<"Can't be divided.\n\n";      else cout<<"Can be divided.\n\n";   }}



原创粉丝点击