多重背包问题(4)

1 问题

(此题在csdn上查找了大量资料)

多重背包问题是01背包问题和完全背包问题的改版,背包的容量为M(不超过背包时全部初始化为0,刚好装满第0容量初始化为0,其他初始化为-1,见后面代码),有n种物品,每种物品的价值为di,重量为wi,最多可以取ci件。问背包所放价值最大是多少。

通俗来讲,小黑推着购物车去超市,购物车容量一定,有n种物品,啤酒饮料矿泉水,瓜子花生八宝粥等,每种物品最多取ni件,比如矿泉水最多拿五瓶,瓜子最多拿十袋等,怎样才能使小黑的购物车种物品的价值最大。

2 O(N*C*M)解法

2.1 问题分析

多重背包问题可以退化为01背包问题来求解,有n种物品,每种物品至多有ci件,则一共可以有n*c个物品(是“个”,不是“种”),每个物品可以选择拿或者不拿,这个问题就和01背包问题一模一样。外层循环是物品个数,内层循环是背包容量(需要逆序,原因见完全背包问题解释)。

2.2 代码编写

#include <iostream>
#include <algorithm>
using namespace std;

void test(){
    int N,M;        //物品种类,背包容量
    cin>>N>>M;
    int* arrW = new int[N+1];    //物品体积数组
    int* arrD = new int[N+1];    //物品价值数组
    int* arrC = new int[N+1];    //每种物品的数量数组
    int* arrM = new int[M+1];    //背包容量数组
    for(int i = 0;i <= M;i++){
    //初始化背包容量数组,若不超过背包容积则全部初始化为0,若刚好装满,则第0项初始化为0,其他均初始化为1;
        arrM[i] = 0;
    }
    
    for(int i = 1;i <= N;i++){
        cin>>arrW[i]>>arrD[i]>>arrC[i];         //初始化物品的体积,价值,数量
    }

    for(int i = 1;i <= N;i++){                  //最外层循环,物品种类
        for(int j = 1;j <= arrC[i];j++){        //中层循环,第i种物品的至多数量
            //for(int k = M;k >= 0;k--){          //内层循环,背包的容量
               // if(arrW[i] <= k){
                    //当作01背包问题来求解
                    //arrM[k] = max(arrM[k],arrM[k-arrW[i]]+arrD[i]);
               // }
            //}
            for(int k = M;k >= arrW[i];k--){
                arrM[k] = max(arrM[k],arrM[k-arrW[i]]+arrD[i]);
            }
        }
    }
    //以上是三层循环,时间复杂度为三数相乘,N*C*M

    cout<<arrM[M]<<endl;
    delete[] arrW;
    delete[] arrD;
    delete[] arrC;
    delete[] arrM;
}

int main()
{
    test();
    return 0;
}

3 O(N*(lgC)*M)解法

3.1 问题分析

对于2中复杂度为N*C*M,如果C过大,则时间复杂度过高,很有可能会超时,对于任何一个整数,都可以用一个二进制来表示,比从1一直遍历到这个数复杂度要低很多。比如数字9,可以用1 2 5来表示,1 2 5可以组成1到9之间的任意一个数,再比如13,可以用1 2 4 6来表示,1 2 4 6(为什么不是1 2 4 8,因为1+2+4+8 > 13,随机组合数已经超过了范围)可以组合表示1到13任何一个数字,比从1直接到13复杂度降低很多。也就是lgC复杂度。

3.2 代码编写

#include <iostream>
#include <algorithm>
using namespace std;

void test(){
    int N,M;                      //物品种类数,背包容量
    cin>>N>>M;
    int* arrW = new int[N+1];     //物品的体积数组
    int* arrD = new int[N+1];     //物品的价值数组
    int* arrC = new int[N+1];     //每种物品的个数数组
    int* arrM = new int[M+1];     //背包容积数组
    for(int i = 0;i <= M;i++){
        arrM[i] = 0;              //此为不超过情况,刚好情况初始化为0 -1 -1 -1 ...
    }

    for(int i = 1;i <= N;i++){
        cin>>arrW[i]>>arrD[i]>>arrC[i];
    }
    
    for(int i = 1;i <= N;i++){
        int tempC = arrC[i];                //把tempC当作13,举例子带入即可
        for(int j = 1;tempC > 0;j = j<<1){    
            int mul = min(tempC,j);         //mul的值为1 2 4 6
            for(int k = M;k >= arrW[i]*mul;k--){    //1 2 4 6带入
                arrM[k] = max(arrM[k],arrM[k-arrW[i]*mul]+arrD[i]*mul);
            }
            tempC = tempC - mul;
        }
    }

    cout<<arrM[M]<<endl;
    delete[] arrW;
    delete[] arrD;
    delete[] arrC;
    delete[] arrM;
}

int main(){
    test();
    return 0;
}
    

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值