当前位置: 首页 > news >正文

算法板子:DP背包问题——01背包、完全背包、多重背包

目录

一、01背包问题

1. 问题描述

2. 代码

(1)朴素做法——使用二维数组f

(2)优化空间做法——将二维数组f降维成一维数组

二、完全背包问题

1. 问题描述 

2. 代码

(1)朴素做法——使用二维数组f

(2)优化空间做法——将二维数组f降维成一维数组

三、多重背包问题

1. 问题描述

2. 代码

(1)朴素做法

(2)使用二进制优化的做法


一、01背包问题

1. 问题描述

有i种物品,背包容量为j,每种物品放背包放入0件或1件

2. 代码

(1)朴素做法——使用二维数组f

状态表示:用二维数组f表示状态;状态转移:不断更新状态,也就是不断更新数组f

#include <iostream>
using namespace std;

const int N = 1000 + 10;

// f[i][j]代表前i个物品在容量为j的背包中的最大价值; f[2][3]=4代表前两个物品在容量为3的背包中的最大价值为4
// w[i]代表第i个物品的体积; w[1]=1代表第1个物品的体积为1
// c[i]代表第i个物品的价值; c[1]=2代表第1个物品的价值为2
int f[N][N], w[N], c[N];

int main()
{
    // 4个物品,背包体积为5
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        int a, b;
        cin >> a >> b;
        w[i] = a, c[i] = b;
    }
    
    // 物品i
    for (int i = 1; i <= n; i ++ )
    {
        // 容量j
        for (int j = 1; j <= m; j ++ )
        {
            // 如果物品i的体积大于容量j,物品i不放进背包
            if (w[i] > j) f[i][j] = f[i - 1][j];
            // 如果物品i的体积小于容量j,考虑是否将物品i放入背包,判断放入背包和不放入背包哪个价值更大
            else f[i][j] = max(f[i - 1][j - w[i]] + c[i], f[i - 1][j]);
        }
    }
    
    // 输出前4个物品在容量为5的背包中的最大价值
    cout << f[n][m] << endl;
    
    return 0;
}
(2)优化空间做法——将二维数组f降维成一维数组

第一步:把f数组的第一维全部删去;第二步:将内层循环变为逆序,j从m循环到w[i],保证j-w[i]大于等于0;第三步: 将f[j]=f[j]等多余部分去掉

// 物品i,容量j
for (int i = 1; i <= n; i ++ )
    for (int j = m; j >= w[i]; j -- )
        f[j] = max(f[j - w[i]] + c[i], f[j]);

二、完全背包问题

1. 问题描述 

有i种物品,背包容量为j,每种物品可以有无限件,每种物品可以往背包中放入0件,1件,2件.....,n件

2. 代码

(1)朴素做法——使用二维数组f
#include <iostream>
using namespace std;

const int N = 1000 + 10;

// f[i][j]代表前i件物品在容量为j的背包中的最大价值
int f[N][N], w[N], c[N];

int main()
{
    // 4种物品,背包容积是5
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        int a, b;
        cin >> a >> b;
        w[i] = a, c[i] = b;
    }
    
    // 物品i(第i种物品)
    for (int i = 1; i <= n; i ++ )
    {
        // 容量j
        for (int j = 1; j <= m; j ++ )
        {
            // 如果物品i的体积大于背包容量j,物品i不能放入背包
            if (w[i] > j) f[i][j] = f[i - 1][j];
            // 如果物品i的体积小于背包容量j,考虑是否物品i放入背包,判断放入背包和不放入背包的最大价值哪个更大
            else f[i][j] = max(f[i][j - w[i]] + c[i], f[i - 1][j]);
        }
    }
    
    // 输出前4种物品在容量为5的背包中的最大价值
    cout << f[n][m] << endl;
    
    return 0;
}
(2)优化空间做法——将二维数组f降维成一维数组

第一步:把f数组的第一维全部删去;第二步:调整j的范围,j从w[i]开始循环,保证j-w[i]大于等于0;第三步:将f[j]=f[j]等多余部分删去。和01背包的唯一不同点就是j是正序。

// 物品i(第i种物品),容量j
for (int i = 1; i <= n; i ++ )
    for (int j = w[i]; j <= m; j ++ )
        f[j] = max(f[j - w[i]] + c[i], f[j]);

三、多重背包问题

1. 问题描述

有i种物品,背包容积为j,每种物品有s件,可以往背包中放0,1,... ,s件物品i——>这是01背包问题的变形,01背包问题是i种物品,每种1件,可以往背包中放0或1件。

2. 代码

(1)朴素做法

步骤:第一步:写出01背包问题的优化空间的板子,写的时候记得多层物品i的数量k的for循环,注意k从0开始到s[i]。第二步:w[i]和v[i]前乘k。第三步:改k的判定条件。

#include <iostream>
using namespace std;

const int N = 100 + 10;

int f[N], w[N], v[N], s[N];

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        w[i] = a, v[i] = b, s[i] = c;
    }
    
    // 物品i,容量j,物品i的数量k
    for (int i = 1; i <= n; i ++ )
        for (int j = m; j >= w[i]; j -- )
            for (int k = 0; k <= s[i] && k * w[i] <= j; k ++ )
                f[j] = max(f[j - k * w[i]] + k * v[i], f[j]);
    
    
    cout << f[m] << endl; 
    
    
    return 0;
}
(2)使用二进制优化的做法——先拆分,再写01模板的板子

拆分——将每种物品的数量按照2的k次方进行拆分:比如第1种物品的体积为1价值为2数量为8,那么就可以就可以将数量8拆分成1,2,4,1; 对于1:体积1,价值2;对于2:体积2,价值4;对于4:体积4,价值8;对于1:体积1,价值2。   

#include <iostream>
using namespace std;

const int N = 2010, M = 11010;

// f[j]中的j代表背包容积,根据题意得j最大为2000
// ww[i]代表第i种物品的体积; vv[i]代表第i种物品的价值; cnt记录有多少种物品,最多有1000logSi种物品,这道题就是1000log2000向上取整再加十为11010
int f[N], ww[M], vv[M], cnt;

int main()
{
    int n, m;
    cin >> n >> m;
    
    for (int i = 1; i <= n; i ++ )
    {
        // 第i种物品的体积,价值,数量
        int a, b, s;
        cin >> a >> b >> s;
        
        // 拆分第i种物品的数量-->将一种物品拆分成多种物品
        for (int j = 1; j <= s; j <<= 1)
        {
            ww[ ++ cnt] = j * a, vv[cnt] = j * b;
            s -= j;
        }
        
        // 如果数量s有剩余,记录下最后一种物品
        if (s) ww[ ++ cnt] = s * a, vv[cnt] = s * b;
    }
    
    // 01背包模板; 物品i,容量j; 注意拆分过后有cnt种物品
    for (int i = 1; i <= cnt; i ++ )
        for (int j = m; j >= ww[i]; j -- )
            f[j] = max(f[j - ww[i]] + vv[i], f[j]);
    
    cout << f[m] << endl;
    
    return 0;
}

相关文章:

  • vue 日期控件 100天内的时间禁用不允许选择
  • luckyexcel 编辑预览excel文件
  • 前端构建URL的几种方法比对,以及函数实现
  • 【泰克生物】蛋白药物适配体筛选技术:定制化治疗的前沿探索
  • 查找文件(linux)
  • 如何利用消费者互动提升新品牌口碑的传播效果?
  • cms里文章页自定义文件名去掉html方法
  • 贷齐乐案例
  • el-table-column字段格式化转换,formatter属性使用
  • 独立开发者系列(42)——MYSQL语句使用和进阶
  • Pod的调度机制
  • 详解Xilinx FPGA高速串行收发器GTX/GTP(11)--详解GTX的示例工程
  • 记录一次绕过 Android 服务端的证书校验的详细过程
  • PyTorch深度学习实战(10)—— 神经网络工具箱nn.Module
  • 二叉树------最小堆,最大堆。
  • 杂项复现-中间件
  • 秃姐学AI系列之:PyTorch模型构造 | 参数管理 | 自定义层 | 读写文件
  • Java反射机制深度解析与实践应用
  • Electron-builder 打包
  • Unity教程(十)Tile Palette搭建平台关卡
  • 海北州委常委、常务副州长桑本履新青海省供销社理事会主任
  • 寒武纪陈天石:公司的产品力获得了行业客户广泛认可,市场有望迎来新增量需求
  • 权益类基金发行回暖,这些老将挂帅新基,谁值得买?
  • 人大新闻教育70年丨16759门课程里的时代密码
  • 普京提议无条件重启俄乌谈判,外交部:我们支持一切致力于和平的努力
  • 西藏日喀则市拉孜县发生5.5级地震,震源深度10公里