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

目录

一、01背包问题

[1. 问题描述](#1. 问题描述)

[2. 代码](#2. 代码)

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

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

二、完全背包问题

[1. 问题描述](#1. 问题描述)

[2. 代码](#2. 代码)

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

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

三、多重背包问题

[1. 问题描述](#1. 问题描述)

[2. 代码](#2. 代码)

(1)朴素做法

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


一、01背包问题

1. 问题描述

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

2. 代码

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

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

cpp 复制代码
#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]等多余部分去掉

cpp 复制代码
// 物品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
cpp 复制代码
#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是正序。

cpp 复制代码
// 物品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的判定条件。

cpp 复制代码
#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。

cpp 复制代码
#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;
}
相关推荐
蓑 羽2 分钟前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
源代码:趴菜4 分钟前
LeetCode63:不同路径II
算法·leetcode·职场和发展
儿创社ErChaungClub20 分钟前
解锁编程新境界:GitHub Copilot 让效率翻倍
人工智能·算法
前端西瓜哥24 分钟前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法
小灰灰爱代码31 分钟前
C++——求3个数中最大的数(分别考虑整数、双精度数、长整数的情况),用函数模板来实现。
开发语言·c++·算法
南加第一划水35 分钟前
Leetcode 每日一题:Evaluate Division
算法·leetcode·职场和发展
逝去的秋风1 小时前
【代码随想录训练营第42期 Day61打卡 - 图论Part11 - Floyd 算法与A * 算法
算法·图论·floyd 算法·a -star算法
zero_one_Machel1 小时前
leetcode73矩阵置零
算法·leetcode·矩阵
BeyondESH2 小时前
Linux线程同步—竞态条件和互斥锁(C语言)
linux·服务器·c++
青椒大仙KI112 小时前
24/9/19 算法笔记 kaggle BankChurn数据分类
笔记·算法·分类