混合三种背包问题

问题
如果将01背包、完全背包、多重背包混合起来。也就是说,有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。应该怎么求解呢?

01背包与完全背包的混合
考虑到在01背包和完全背包中最后给出的伪代码只有一处不同,故如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用顺序或逆序的循环即可,复杂度是O(VN)。伪代码如下:

for i=1..N
    if 第i件物品是01背包
        for v=V..0
            f[v]=max{f[v],f[v-c[i]]+w[i]};
    else if 第i件物品是完全背包
        for v=0..V
            f[v]=max{f[v],f[v-c[i]]+w[i]};

再加上多重背包
如果再加上有的物品最多可以取有限次,那么原则上也可以给出O(VN)的解法:遇到多重背包类型的物品用单调队列解即可。但如果不考虑超过NOIP范围的算法的话,用多重背包中将每个这类物品分成O(log n[i])个01背包的物品的方法也已经很优了。

 cin>>x>>y>>s;         
while (s>=t){            
weight[++n1]=x*t;            
value[n1]=y*t;             
s-=t;            
t*=2;        
}        
weight[++n1]=x*s;        
value[n1]=y*s;  //把s以2的指数分堆:1,2,4,…,2^(k-1),s-2^k+1,

小结
有人说,困难的题目都是由简单的题目叠加而来的。这句话是否公理暂且存之不论,但它在本讲中已经得到了充分的体现。本来01背包、完全背包、多重背包都不是什么难题,但将它们简单地组合起来以后就得到了这样一道一定能吓倒不少人的题目。但只要基础扎实,领会三种基本背包问题的思想,就可以做到把困难的题目拆分成简单的题目来解决

旅行者有一个容量为V公斤的背包

 

n :物品种数

w :物品重量

c :物品价值

p :物品可以拿取的数目(0表示可取无穷多件)

 

输入样例:

10 3

2 1 0

3 3 1

4 5 4

 

输出样例:

11

//完全背包,同种物件多次拿取

for(int j=w[i];j<=m;j++)//从小到大的叠加多次选择
                f[j]=max( f[j],f[j-w[i]]+c[i] );

 for(int j=1;j<=p[i];j++)     //01背包和多重背包,拿的次数
            
                for(int k=m;k>=w[i];k--)//从大到小的空间的每次选一个,从后面值结合现值选出最大值
                    f[k]=max( f[k],f[k-w[i]]+c[i] );

 

多重背包 变 01背包,完全背包不变

#include<cstdio>
#include<iostream>
using namespace std;
int m,n;
int w[31],c[31],p[31];
int f[201];

int max(int x,int y)
{
    if(x<y)
        return y;
    else
        return x;

}

int main()
{
    scanf("%d%d",&m,&n);
    int n1=0;
    int x,y,s;
    for(int i=1; i<=n; i++)//都变成了01背包问题和完全背包问题
    {
        cin>>x>>y>>s;
        if(s>1)
        {
            int t=1;

            while (s>=t)
            {
                w[++n1]=x*t;
                c[n1]=y*t;
                p[n1]=1;

                s-=t;
                t*=2;
            }
            w[++n1]=x*s;
            c[n1]=y*s;
            p[n1]=1;
        }
        else
        {
            w[++n1]=x;
            c[n1]=y;
            p[n1]=s;
        }
    }

    for(int i=1; i<=n1; i++)
    {
        if(p[i]==0)     //完全背包
        {
            for(int j=w[i]; j<=m; j++) //从小到大的叠加多次选择
                f[j]=max( f[j],f[j-w[i]]+c[i] );
        }
        else //01背包
        {
            //for(int j=1; j<=p[i]; j++)   //多重背包
                for(int k=m; k>=w[i]; k--) //从大到小的逐一选择,从后面值结合现值选出最大值
                    f[k]=max( f[k],f[k-w[i]]+c[i] );
        }

    }
    for(int i=1; i<=m; i++)
    printf("%d\n",f[i]);
    printf("%d",f[m]);
    return 0;
}


 

#include<cstdio>
#include<iostream>
using namespace std;
int m,n;
int w[31],c[31],p[31];
int f[201];

int max(int x,int y)
{
    if(x<y)return y;
    else return x;

}

int main(){
    scanf("%d%d",&m,&n);
    
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&w[i],&c[i],&p[i]);

 

    for(int i=1;i<=n;i++){
        if(p[i]==0)     //完全背包
        {
            for(int j=w[i];j<=m;j++)//从小到大的叠加多次选择
                f[j]=max( f[j],f[j-w[i]]+c[i] );
        }
        else
        {    
            for(int j=1;j<=p[i];j++)     //01背包和多重背包
            
                for(int k=m;k>=w[i];k--)//从大到小的逐一选择,从后面值结合现值选出最大值
                    f[k]=max( f[k],f[k-w[i]]+c[i] );

        }
    }
    printf("%d",f[m]);
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值