多重背包问题的优化 学习笔记 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背包模板的第二层循环体积从大到小遍历

相关推荐
GlueNa2SiO35 小时前
03-Flask模板引擎Jinja2详解
笔记·python·flask
Capricorn19886 小时前
解决全网抄 Karpathy 导致的 LLM Wiki 污染?基于知芽 Notebook Skill 的 raw/ 笔记法排障指南
大数据·论文阅读·人工智能·笔记·论文笔记
壹玖玖肆7 小时前
openGauss使用笔记
笔记
宵时待雨7 小时前
linux笔记归纳17:传输层协议UDP
linux·笔记·udp
噜~噜~噜~8 小时前
操作系统笔记-2.3.2.1 进程互斥的软件实现方法
笔记·操作系统
小弥儿9 小时前
GitHub今日热榜 | 2026-08-26:AI 大脑与知识管理扎堆
人工智能·学习·开源·github
荷蒲9 小时前
【小白量化Qbuddy】利用AI学习中文Python
人工智能·python·学习
云贝教育-郑老师9 小时前
【麒麟服务器系统交付:一个DBA的六年信创实战笔记】
服务器·笔记·dba
中科高级技工学校9 小时前
乌鲁木齐中医康复学习经历分享
大数据·学习
玖玥拾10 小时前
LeetCode 392 判断子序列
笔记·算法·leetcode