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

相关推荐
小淮AI5 小时前
从“刷题”到“追问”:AI课堂正在重塑哪些学习旧习惯?
人工智能·学习
wixzjsh6 小时前
TCP通信与HTTP协议学习笔记:粘包、三次握手、四次挥手与C/S、B/S模型
学习·tcp/ip·http
ZhiMuZhiLing6 小时前
拓客软件学习成本实测方法论:从注册到出第一批名单要多久
学习·流量运营·用户运营
程序员大雄学编程7 小时前
微积分46. 无穷级数四大核心性质:从理论到实践的全方位解析
python·学习·微积分·学习工具
LuminousCPP8 小时前
数据结构-二叉树(六):BFS层序遍历与完全二叉树判断|复用链式队列 + (N_0=N_2+1) 性质证明
c语言·数据结构·笔记·算法·二叉树·宽度优先
指针常量9 小时前
【RL系列文章】难样本学习等
学习·大语言模型·rl·后训练
wuyk5559 小时前
从零吃透Modbus通信|第7章:终极工程整合(模块化架构、双模式主机从机、RTOS适配、量产级模板)
c语言·stm32·学习·架构
一条破秋裤10 小时前
STM32 学习笔记:PWM 驱动 LED 呼吸灯与舵机代码
笔记·stm32·学习
kyrie_sakura12 小时前
python学习笔记11 -- 进程和线程
笔记·python·学习
自小吃多12 小时前
PCB Editor软件差分对添加与设置笔记
笔记·嵌入式硬件