动态规划 -- #背包问题之多重背包和完全背包

若没有相关知识基础,可以先看看下面文章哦🤗👇
完全背包问题
01背包问题


多重背包

在这里插入图片描述

朴素解法

每个物品数量只有固定数量个,只从前i个物品中选,并且总体积不超过j的选法
解法跟完全背包差不多,只不过我们k的范围是有限制的,其解法就跟完全背包差不多

#include<iostream>
#include<algorithm>

using namespace std;

const int N =110;
int s[N],v[N],w[N];
int n,m;
int f[N][N];

int main(){
    cin >> n >> m;
    
    for(int i=1; i<=n; i++){
        cin >> v[i] >> w[i] >> s[i]; 
    }

    for(int i=1; i<=n; i++){
        for(int j=1; j<=m; j++){
            for(int k=0; k<=s[i] && k*v[i] <= j; k++){
                f[i][j]=max(f[i-1][j],f[i-1][j-v[i]*k]+w[i]*k);
            }
        }
    }
    cout<<f[n][m];

    return 0;
}

二进制优化

  • 在上述枚举物品数量的时候,我们需要从0一直枚举到其最大数量
    如果物品数量有很多的话,我们这样的效率就会很低

    如果物品有1023个,我们不需要枚举到1023个
    我们采用一种打包的方式
    如 1 2 4 8…512 这几种"包裹"
    选择1,2可以凑出来0~3数量,再选一个4就能凑出来0~7数量,
    以此类推->我们选择1 2 4 8 16 32 64 128 256 512 就能凑出来1023这种数量

    每一个选择可以选0次或者1次,那么可以看作一个01背包问题

    我们只需要计算每一个包裹的重量和价值即可

    • 如何打包呢,如果物品数量有S个
      我们枚举2的0,1,2,3…k次方直至2的k次方大于物品剩余数量
      举例 物品 数量1000个
      那么我们只需要分成 1 2 4 8 16 32 64 128 256 489 这几种包裹
#include<iostream>
#include<algorithm>

using namespace std;

const int N =25000,M=2010;

int n,m;

int v[N],w[N];

int f[N];

int main(){
    cin >> n >> m;

    int cnt=0;//记录分成了几组

    for(int i=1; i<=n; i++){
        int a,b,s;
        //体积 价值 数量
        cin >> a >> b >> s;
        //记录当前是2的几次方
        int k=1;
    	while(k<=s){
            cnt++;
            v[cnt]=a*k;//这一组的重量
            w[cnt]=b*k;//这一组的价值
            s-=k;//在总数里减去这组的数量
            k*=2;
    }
        if(s>0){
            cnt++;
            v[cnt]=a*s;
            w[cnt]=b*s;
        }
    }
    //最后没分完的话别忘了再单开一组
    n = cnt;
    //最后就是要在n组里选择
    for(int i=1; i<=n; i++){
        for(int j=m; j>=v[i]; j--){
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }
    cout << f[m] << endl;
    return 0;
}

分组背包

在这里插入图片描述

物品会分组,每一组里面只能选一个物品,如水果组选了苹果不能选香蕉
我们分组背包需要的是枚举第i组物品选哪个或者不选

#include<iostream>
#include<algorithm>

using namespace std;

const int N =110;
int v[N][N],w[N][N],s[N];
int n,m;
int f[N];

int main(){
    //物品组数,背包容量
    cin >> n >> m;
	//n组数据,Si表示第i个物品组的物品数量
    for(int i=1; i<=n; i++){
        //第i个物品组的物品数量
        cin >> s[i];
        //第i个物品组的第j个物品的重量和价值
        for(int j=1; j<=s[i]; j++){
            cin >> v[i][j] >> w[i][j];
        }
    }
	//枚举n个物品组
    for(int i=1; i<=n; i++){
        //这里是01背包的一维解法,需要倒序遍历背包容量
        for(int j=m; j>=0; j--){
            //第i组第k个物品的重量和价值
            for(int k=1; k<=s[i]; k++){
                //如果背包能装得下
                if(v[i][k] <= j)f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
            }
        }
    }
    cout << f[m] << endl;

    return 0;
}

结语


🌻编写本篇文章目的是笔者想以输出的形式进行学习,顺便记录学习点滴🌻

🌹 如果本篇文章对你有帮助的话那就点个赞吧👍🌹

😇 本篇文章存在多处不足,如有修改意见,可以私信或者评论哦,还望海涵 😇

文章内容部分参考于Acwing


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值