多重背包问题的优化 学习笔记 AcWing 5. 多重背包问题 II(算法基础课)

乘法原理

百度百科

乘法原理是说把多个步骤的所有方法相乘,表示整个事件所有可能的解决方法

原题

有 N� 种物品和一个容量是 V� 的背包。

第 i� 种物品最多有 si�� 件,每件体积是 vi��,价值是 wi��。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。

输出最大价值。

输入格式

第一行两个整数,N,V�,�,用空格隔开,分别表示物品种数和背包容积。

接下来有 N� 行,每行三个整数 vi,wi,si��,��,��,用空格隔开,分别表示第 i� 种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

0<N≤10000<�≤1000

0<V≤20000<�≤2000

0<vi,wi,si≤20000<��,��,��≤2000

提示:

本题考查多重背包的二进制优化方法。

输入样例
复制代码
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
复制代码
10

|-----------------|
| 难度:中等 |
| 时/空限制:1s / 64MB |
| 总通过数:75903 |
| 总尝试数:164479 |
| 来源:背包九讲 , 模板题 |
| 算法标签 |


挑战模式

原题链接

传送门

代码

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N=11000+10,M=2000+10;
int v[N],w[N];
int f[M];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    
    int cnt=0;
    for(int i=0;i<n;i++)
    {
        int a,b,s;
        scanf("%d%d%d",&a,&b,&s);
        int k=1;
        while(k<=s)
        {
            cnt++;
            v[cnt]=k*a;
            w[cnt]=k*b;
            s-=k,k*=2;
        }
        if(s>0)
        {
            cnt++;
            v[cnt]=s*a;
            w[cnt]=s*b;
        }
    }
    
    n=cnt;
    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]);
        }
    }
    
    printf("%d\n",f[m]);
    return 0;
}

总结

一、二进制优化

1.不优化的情况的时间复杂度是N^3,在数据范围比较大的时候会超时,所以我们选择二进制优化,把一维的N优化为logN,时间复杂度变为N^2*logN,可以通过这道题

2.思路是,给定了物品数目s,可以选择0件,1件,2件,......,s件物品,一个一个循环的话需要循环s+1次才能表示所有的情况,如果我们使用二进制的话,int范围内都只需要32位数字,就可以表示所有数字,数据范围是2000,只需要最多11个数字,2^11=2048,2^0,2^1,2^2,...2^11,二进制表示本质和01背包就很相似,每一个数位选择0或者是1,从而可以表示所有数字

3.注意一个一般化的情况,物品的件数s不是2的整数次幂,比如数字10,2^0=1,2^1=2,2^2=4,2^3=8,0+1+2+4+8>10,如果选择到数字2^3=8,超过了我们最多可以选择的件数10,这会造成理论上可以选择多件物品,但是实际上只有10件物品可以选择,所以我们只能选择到2^2=4,0+1+2+4=7,再补充一个数字10-7=3,原来的0,1,2,4可以表示0~7范围的所有数字,加上数字3之后,可以表示0~10范围的所有数字,0,1,2,4表示0~7范围内所有数字的方法就是二进制计数,每一个数位是否选择该数字

      • -:000 0 0
        • :001 1 1
      • -:010 2 2
      • -:011 3 1+2
      • -:100 4 4
      • -:101 5 4+1
        • :110 6 4+2
        • :111 7 4+2+1

二、代码细节

1.表示物品的数组的容量是 1000*11(二进制优化),表示答案的数组的容量是2000,都增加10防止数组越界

2.本质是把一件物品拆成多组物品,是否选择该组物品,从而把多重背包转换成01背包来进行求解

3.用一个计数器来存数组下标,和单链表的dix类似,while循环之后加一个if特判,表示最后一种情况,和质因数分解类似,也是判断最后处理的结果

4.原来的n件物品变成了现在的cnt件物品,因为计数器从1开始作为数下标存储物品的体积和价值,所以套用01背包模板的时候要从1开始遍历,01背包模板的第二层循环体积从大到小遍历

相关推荐
wishfly35 分钟前
vscode - 笔记
ide·笔记·vscode
黄鹂绿柳2 小时前
Vue+Vite学习笔记
vue.js·笔记·学习
985小水博一枚呀6 小时前
【AI大模型学习路线】第二阶段之RAG基础与架构——第七章(【项目实战】基于RAG的PDF文档助手)技术方案与架构设计?
人工智能·学习·语言模型·架构·大模型
FakeOccupational6 小时前
计算机科技笔记: 容错计算机设计05 n模冗余系统 TMR 三模冗余系统
笔记·科技
hello1114-7 小时前
Redis学习打卡-Day3-分布式ID生成策略、分布式锁
redis·分布式·学习
小Tomkk7 小时前
2025年PMP 学习二十 第13章 项目相关方管理
学习·pmp·项目pmp
独行soc8 小时前
2025年渗透测试面试题总结-百度面经(题目+回答)
运维·开发语言·经验分享·学习·面试·渗透测试·php
海棠蚀omo8 小时前
C++笔记-红黑树
开发语言·c++·笔记
ysy16480672398 小时前
03算法学习_977、有序数组的平方
学习·算法
FAREWELL000758 小时前
Unity学习总结篇(1)关于各种坐标系
学习·unity·c#·游戏引擎