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

相关推荐
电子云与长程纠缠16 分钟前
Blender入门学习09 - 制作动画
学习·blender
电子云与长程纠缠18 分钟前
Blender入门学习10 - 曲线绘制
学习·blender
下午见。35 分钟前
C语言结构体入门:定义、访问与传参全解析
c语言·笔记·学习
im_AMBER37 分钟前
React 16
前端·笔记·学习·react.js·前端框架
lkbhua莱克瓦241 小时前
Java基础——常用算法5
java·开发语言·笔记·github
摇滚侠2 小时前
Spring Boot3零基础教程,响应式编程的模型,笔记109
java·spring boot·笔记
YuanDaima20483 小时前
[CrewAI] 第5课|基于多智能体构建一个 AI 客服支持系统
人工智能·笔记·多智能体·智能体·crewai
民乐团扒谱机3 小时前
实验室安全教育与管理平台学习记录(七)网络安全
学习·安全·web安全
许长安4 小时前
c/c++ static关键字详解
c语言·c++·经验分享·笔记
蒙奇D索大4 小时前
【11408学习记录】考研英语长难句精析:三步拆解真题复杂结构,轻松攻克阅读难关!
笔记·学习·考研·改行学it