动态规划」详解背包问题及实践(附C++代码)

这篇具有很好参考价值的文章主要介绍了动态规划」详解背包问题及实践(附C++代码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、 背包问题简介

1. 背包问题是什么

背包问题是一个经典的组合优化问题,它可以被抽象为一个把物品放入背包中的过程,以求最终背包中物品价值的最大化。

2. 背包问题的分类

常见的背包问题主要分为以下三种:

  1. 01背包问题:每种物品最多只能装一次。
  2. 完全背包问题:每种物品可以无限次装入背包中。
  3. 多重背包问题:每种物品有限制次数可以装入背包中。

二、 0/1背包问题定义

1. 0/1背包问题的定义

0/1背包问题是一个经典的动态规划问题,指的是有n个物品和一个容量为W的背包,每个物品只能选择装入一次或不装入。在装入背包的物品总重量不能超过W的前提下,选择物品装入背包中,使得背包中物品的总价值最大。

2. 动态规划算法解决0/1背包问题

对于0/1背包问题,我们可以采用动态规划算法来解决。具体的解决思路如下:

  • 定义状态:用dp[i][j]表示前i个物品,容量为j时的最大价值。
  • 状态转移方程:对于第i个物品,我们有两种选择,可以选择将其放入背包中,也可以选择不将其放入背包中。因此有如下状态转移方程:

dp[i][j] = max(dp[i-1][j],dp[i-1][j-w[i]] + v[i])

其中w[i]是第i个物品的重量,v[i]是第i个物品的价值。

通过上述状态转移方程,我们可以求出前i个物品,容量为j时的最大价值。最后返回dp[n][W]即可。

3. 代码示例

下面是一个基于动态规划的0/1背包问题的代码实现

#include <vector>
#include <iostream>
using namespace std;

int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); // 定义状态

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= W; j++) {
            if(j < w[i - 1]) { // 当前物品不能装入背包
                dp[i][j] = dp[i - 1][j];
            } else { // 当前物品可以装入背包
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i - 1]] + v[i - 1]); // 状态转移方程
            }
        }
    }

    return dp[n][W]; // 返回前n个物品,容量为W时的最大价值
}

int main() {
    vector<int> w = {2, 3, 4}; // 物品重量
    vector<int> v = {4, 5, 6}; // 物品价值
    int W = 5; // 背包容量

    cout << "The maximum value is: " << knapsack(w, v, W) << endl; // 输出最大价值

    return 0;
}

通过动态规划算法,该程序可以计算最大价值并输出,从而解决0/1背包问题。

三、 完全背包问题

1. 完全背包问题的定义

完全背包问题是一个经典的动态规划问题,在0/1背包问题的基础上进行了扩展,它指的是有n个物品和一个容量为W的背包,每个物品可以选择装入多次,装入物品的总重量不能超过背包容量W,目标是装入的物品总价值最大

2. 动态规划算法解决完全背包问题

对于完全背包问题,我们同样可以采用动态规划算法来解决。与0/1背包问题不同的是,在完全背包问题中,每一个物品可以选择装入多次,因此需要重新定义状态和转移方程。

  • 定义状态:用dp[i][j]表示前i个物品,容量为j时的最大价值。
  • 状态转移方程:对于第i个物品,我们可以选择装入它或不装入它。如果选择装入它,则背包容量会减少w[i-1],同时总价值加上v[i-1]。因此有如下状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i][j-w[i-1]] + v[i-1])

其中w[i-1]是第i个物品的重量,v[i-1]是第i个物品的价值。

需要注意的是,对于完全背包问题,状态转移方程中的dp[i - 1][j - w[i - 1]]已经变为了dp[i][j - w[i - 1]],因为每个物品可以装入多次。

3. 代码示例

下面是一个基于动态规划的完全背包问题的代码实现

#include <vector>
#include <iostream>
using namespace std;

int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); // 定义状态

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= W; j++) {
            if(j < w[i - 1]) { // 当前物品不能装入背包
                dp[i][j] = dp[i - 1][j];
            } else { // 当前物品可以装入背包
                dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i - 1]] + v[i - 1]); // 状态转移方程
            }
        }
    }

    return dp[n][W]; // 返回前n个物品,容量为W时的最大价值
}

int main() {
    vector<int> w = {2, 3, 4}; // 物品重量
    vector<int> v = {4, 5, 6}; // 物品价值
    int W = 5; // 背包容量

    cout << "The maximum value is: " << knapsack(w, v, W) << endl; // 输出最大价值

    return 0;
}

通过动态规划算法,该程序可以计算最大价值并输出,从而解决完全背包问题。

四、 多重背包问题

1. 多重背包问题的定义

多重背包问题同样是一个经典的动态规划问题,它指的是有n个物品和一个容量为W的背包,每个物品有对应的数量限制和重量以及价值,物品j最多能装k次,装入物品的总重量不能超过背包容量W,目标是装入的物品总价值最大。

2. 动态规划算法解决多重背包问题

与完全背包问题类似,多重背包问题也可以采用动态规划算法来解决。但是需要注意的是,与完全背包问题不同的是,多重背包问题中每个物品有着数量限制,所以需要重新定义状态和转移方程。

  • 定义状态:用dp[i][j]表示前i个物品,容量为j时的最大价值。
  • 状态转移方程:对于第i个物品,我们可以选择装入它或不装入它。如果选择装入它,需要考虑它的数量限制k[i-1],背包容量会减少多个w[i-1],同时总价值加上多个v[i-1]。因此有如下状态转移方程:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-k[i-1]*w[i-1]]+k[i-1]v[i-1], dp[i-1][j-2k[i-1]w[i-1]]+2k[i-1]*v[i-1], …)

其中k[i-1]是第i个物品的数量限制,w[i-1]是第i个物品的重量,v[i-1]是第i个物品的价值。

需要注意的是,转移方程中的数量部分需要列举每一种可能的数量情况,才能求出最大价值。

3. 代码示例

下面是一个基于动态规划的多重背包问题的代码实现

#include <vector>
#include <iostream>
using namespace std;

int knapsack(vector<int>& w, vector<int>& v, vector<int>& k, int W) {
    int n = w.size();
    vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0)); // 定义状态

    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= W; j++) {
            for(int t = 0; t <= k[i - 1] && t*w[i - 1] <= j; t++) { // 列举每一种可能的数量情况
                dp[i][j] = max(dp[i][j], dp[i - 1][j - t*w[i - 1]] + t*v[i - 1]); // 状态转移方程
            }
        }
    }

    return dp[n][W]; // 返回前n个物品,容量为W时的最大价值
}

int main() {
    vector<int> w = {2, 3, 4}; // 物品重量
    vector<int> v = {4, 5, 6}; // 物品价值
    vector<int> k = {2, 3, 1}; // 物品数量限制
    int W = 10; // 背包容量

    cout << "The maximum value is: " << knapsack(w, v, k, W) << endl; // 输出最大价值

    return 0;
}

通过动态规划算法,该程序可以计算最大价值并输出,从而解决多重背包问题。

五、 混合背包问题

1. 混合背包问题的定义

混合背包问题是指有n个物品和容量为W的背包,每个物品有对应的价值和重量以及选择方式(0/1、多重、完全),要求在装入物品的总重量不超过背包容量的前提下,将背包的总价值最大化。

2. 动态规划算法解决混合背包问题

混合背包问题同时拥有0/1、多重和完全三种选择方式,而前两者的解法已经在前面的问题中讨论过。因此,我们只需考虑如何通过动态规划算法解决混合背包问题中的完全背包问题。

  • 定义状态:用dp[i][j]表示前i个物品,容量为j时的最大价值。
  • 状态转移方程:对于第i个物品,若采用完全背包的方式装入背包,则有如下状态转移方程:

dp[i][j] = max(dp[i][j], dp[i-1][j-kw[i-1]]+kv[i-1])

其中k为第i个物品的选择数量,w[i-1]为第i个物品的重量,v[i-1]为第i个物品的价值。

需要注意的是,完全背包问题中采用的是无限制的选取,因此我们只需在状态转移方程中将k从1到j/w[i-1]的所有情况都遍历一遍即可。

  • 同样需要记得将前面两种选择方式的结果进行合并。

3. 代码示例

下面是一个基于动态规划的混合背包问题的代码实现

#include <vector>
#include <iostream>
using namespace std;

int knapsack(vector<int>& w, vector<int>& v, vector<int>& c, int W) {
    int n = w.size();
    vector<int> dp(W + 1, 0); // 定义状态,此处只需要开一个一维数组,因为混合背包只需要合并前两种选择方式的结果即可

    for(int i = 1; i <= n; i++) {
        if(c[i - 1] == 0) { // 0/1选择方式
            for(int j = W; j >= w[i - 1]; j--) {
                dp[j] = max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
            }
        } else if(c[i - 1] == 1) { // 多重选择方式
            for(int j = w[i - 1]; j <= W; j++) {
                dp[j] = max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
            }
        } else { // 完全选择方式
            for(int j = 1; j <= W; j++) {
                for(int k = 1; k <= j / w[i - 1]; k++) { // 遍历每一个可能的数量情况
                    dp[j] = max(dp[j], dp[j - k * w[i - 1]] + k * v[i - 1]);
                }
            }
        }
    }

    return dp[W]; // 返回前n个物品,容量为W时的最大价值
}

int main() {
    vector<int> w = {2, 3, 4}; // 物品重量
    vector<int> v = {4, 5, 6}; // 物品价值
    vector<int> c = {0, 1, 2}; // 物品选择方式
    int W = 10; // 背包容量

    cout << "The maximum value is: " << knapsack(w, v, c, W) << endl; // 输出最大价值

    return 0;
}

通过动态规划算法,该程序可以计算最大价值并输出,从而解决混合背包问题。

六、c++ 背包问题的优化

1. 背包问题的常见优化策略

在实际应用中,我们通常需要通过优化背包问题的算法以降低时间和空间复杂度,提高程序运行效率。以下是背包问题的常见优化策略:

  • 优化状态转移方程:

在计算某个状态时,我们可以通过前面已经计算出来的状态来简化目标状态的计算。例如,我们可以将0/1背包问题的状态转移方程优化为:

dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1])

即将原来的dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i-1]] + v[i-1], dp[i-2][j], dp[i-2][j-w[i-1]] + v[i-1]…)转换为只考虑前一行dp[i-1][ ]的值。

  • 优化空间复杂度:

使用滚动数组可以将背包问题中的二维数组降为一维数组,从而只需要开O(W)的空间。

  • 优化搜索顺序:

将物品按照单位重量价值从大到小排序,可以让后面的物品更有可能被选择,从而避免搜索过多无用的状态。

2. 代码示例

下面是一个基于优化策略的背包问题的代码实现

#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;

// 滚动数组版本的0/1背包问题
int knapsack(vector<int>& w, vector<int>& v, int W) {
    int n = w.size();
    vector<int> dp(W + 1, 0);

    for(int i = 1; i <= n; i++) {
        for(int j = W; j >= w[i - 1]; j--) {
            dp[j] = max(dp[j], dp[j - w[i - 1]] + v[i - 1]);
        }
    }

    return dp[W];
}

// 根据单位重量价值排序后的0/1背包问题
struct Item {
    int w, v;
    double unitValue; // 单位重量价值
};

bool cmp(Item a, Item b) {
    return a.unitValue > b.unitValue; // 按照单位重量价值从大到小排序
}

int knapsack2(vector<Item>& items, int W) {
    int n = items.size();
    sort(items.begin(), items.end(), cmp);
    vector<int> dp(W + 1, 0);

    for(int i = 0; i < n; i++) {
        for(int j = W; j >= items[i].w; j--) {
            dp[j] = max(dp[j], dp[j - items[i].w] + items[i].v);
        }
    }

    return dp[W];
}

int main() {
    vector<int> w = {2, 3, 4}; // 物品重量
    vector<int> v = {4, 5, 6}; // 物品价值
    int W = 5; // 背包容量

    cout << "The maximum value is: " << knapsack(w, v, W) << endl; // 输出最大价值

    vector<Item> items = {{2, 4}, {3, 5}, {4, 6}};
    cout << "The maximum value is: " << knapsack2(items, W) << endl; // 输出最大价值

    return 0;
}

使用滚动数组优化后的0/1背包问题和通过排序优化后的0/1背包问题分别被实现,并输出了最大价值。

七、背包问题的应用

1. 背包问题在实际生活中的应用

背包问题是一个经典的组合优化问题,可以用于很多实际生活场景中,例如:

  • 快递员在派送物品时,需要用最少的次数将所有物品全部送出去,即可把问题转化为背包问题。
  • 旅行者要将一定数量的行李装在行李箱或背包里,而行李箱或背包有一个特定的容量,旅行者需要在有限的容量内带走价值最高的行李。
  • 设计带宽优化问题,选择一定数量的数据包来传输,使得总价值最大,但不超过宽带承载能力。

2. 代码示例

下面是一个背包问题的代码示例

def knapsack(weights, values, capacity):
    """
    使用动态规划思想解决背包问题
    :param weights: 物品重量列表
    :param values: 物品价值列表
    :param capacity: 背包容量
    :return: 背包装物品的最大价值
    """
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        for j in range(1, capacity + 1):
            if j < weights[i - 1]:
                dp[i][j] = dp[i - 1][j]
            else:
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
    return dp[n][capacity]


if __name__ == '__main__':
    weights = [2, 3, 4]  # 物品重量
    values = [4, 5, 6]  # 物品价值
    capacity = 5  # 背包容量
    print("背包最大价值为:", knapsack(weights, values, capacity))

以上代码使用了动态规划的思想解决了背包问题,通过构建dp数组记录状态转移结果。在实现过程中,根据物品重量和背包容量的大小进行判断,得出最大价值。

八、 背包问题结语

1. 背包问题的意义和价值

背包问题是一类经典的组合优化问题,其在工业、经济、科学和技术等各个方面有着广泛的应用。一个更贴切生活的例子是,假设你有一个最大容量为 V V V 的背包,有 n n n 个物品,第 i i i 个物品的重量为 w i w_i wi,价值为 v i v_i vi。现在你要从这 n n n 个物品中选择一部分放入背包中,使得这些物品的总重量不超过 V V V,并且价值最大。这就是背包问题的典型描述。

背包问题的求解过程可以使用各种方法,如贪心、动态规划等。其中,动态规划是最常用的求解方法,常见的动态规划解法包括 0/1 背包问题、完全背包问题和多重背包问题等。

2. 学习建议

如果想要深入学习背包问题,可以从以下两个方面入手:

  • 了解各种求解算法的思想与具体实现。背包问题的各个求解算法具有不同的适用范围和选材方式,对不同类型的问题适用的算法也不同。因此,了解并熟练掌握各种求解算法的思想和具体实现方法,可以更好地解决实际问题。
  • 刷题练习。虽然背包问题的求解方法多种多样,但是题目的套路和解题思路却有一定的共性。不管是初学者还是进阶者,通过不断刷题和实践,学习更多的算法思想和解题技巧,对于学习和掌握背包问题都是非常有帮助的。

作为一名c++程序员,可以在实现背包问题的过程中,多注意以下几点:文章来源地址https://www.toymoban.com/news/detail-441936.html

  • 注意使用STL库函数和语法糖,这可以提高程序开发效率。
  • 注意代码的易读性和可维护性,可以采用面向对象的方式对代码进行重构。
  • 注意C++语言的强类型和数组越界等问题。

到了这里,关于动态规划」详解背包问题及实践(附C++代码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【动态规划】背包问题详解

    动态规划(Dynamic Pogramming,简称dp)是运筹学的一个分支,是求解决策过程最优化的数学方法。 背包问题 则是dp问题里很常见的一类。本篇文章来详解一下背包问题。 动态规划的理解方式有很多种,这里讲述的是yxc老师的闫氏dp法,个人认为是最好的理解方式并且非常好用。

    2024年02月06日
    浏览(44)
  • 动态规划-背包问题详解

    首先给出背包的容量,接着: 01背包问题:给出每个物品的体积和质量,每个物品最多只能使用一次 完全背包问题:给出每个物品的体积和质量,每个物品可以无限次使用 多重背包问题:给出每个物品的体积、质量和数量,每个物品使用量必须在每个物品给定的数量之类 分

    2024年01月24日
    浏览(37)
  • C++ DP算法,动态规划——背包问题(背包九讲)

    有N件物品和一个容量为 V V V 的背包。放入第i件物品耗费的空间是 C i C_i C i ​ ,得到的价值是 W i W_i W i ​ 。 求解将哪些物品装入背包可使价值总和最大。 这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。 用子问题定义状态:即 F [ i , v ] F[i, v] F

    2024年02月16日
    浏览(50)
  • 动态规划-----背包类问题(0-1背包与完全背包)详解

    目录 什么是背包问题? 动态规划问题的一般解决办法: 0-1背包问题: 0 - 1背包类问题  分割等和子集:  完全背包问题:  完全背包类问题 零钱兑换II: 背包问题(Knapsack problem)是一种组合优化的NP完全问题。 问题可以描述为:给定一组物品,每种物品都有自己的重量和价格

    2024年04月17日
    浏览(35)
  • 01背包问题----动态规划 -----python代码、优化

    问题描述: 容量为C的背包选择装物品,有n个物品,它们有各自的体积wi和价值vi,如何让背包里装入的物品具有最大价值? 解题思路: 也就是n个物品选择装入背包,每个物品都有两种选择,是(1)或否(0), 建模:       xi表示当前第i个物品是否选择,xi取值为(0,1)

    2024年02月04日
    浏览(54)
  • C++算法初级11——01背包问题(动态规划2)

    辰辰采药 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时

    2024年02月02日
    浏览(50)
  • 动态规划详细讲解c++|经典例题讲解认识动态规划|0-1背包问题详解

    uu们,你们好!这次的分享是动态规划,其中介绍了动态规划的相关概念和做题模板(三要素),同时为了uu们对动态规划方法有更加形象的认识,特地找了两个经典问题,和大家一起分析。并且呢,为了大家检验自己的学习成果,我专门在常用的oj上为uu们找到了相关题目的

    2024年04月11日
    浏览(60)
  • 背包问题算法全解析:动态规划和贪心算法详解

    计算机背包问题是动态规划算法中的经典问题。本文将从理论和实践两个方面深入探讨计算机背包问题,并通过实际案例分析,帮助读者更好地理解和应用该问题。 背包问题是一种经典的优化问题。有的时候我们需要将有一堆不同重量或者体积的物品放入背包,但是背包容量

    2024年02月09日
    浏览(49)
  • 动态规划01背包问题-代码随想录-刷题笔记

    有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。 每件物品只能用一次 ,求解将哪些物品装入背包里物品价值总和最大。 二维dp数组01背包 确定dp数组以及下标的含义 是使用二维数组,即 dp[i][j] 表示从下标为[0-i]的物品里任意取,

    2024年02月07日
    浏览(58)
  • 回溯法,分支限界法,动态规划法求解0-1背包问题(c++)

    问题描述 给定n种物品和一背包。物品i的重量是wi0,其价值为vi0,背包的容量为c。问应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 搜索空间: 子集树(完全二叉树) 约束函数(进包用): 如果当前背包中的物品总重量是cw,前面k-1件物品都已经决定是否进包,那

    2024年02月10日
    浏览(57)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包