一篇文章吃透背包问题!!!【动态规划】

这篇具有很好参考价值的文章主要介绍了一篇文章吃透背包问题!!!【动态规划】。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是背包问题?

  • 简单来说就是:一个小偷背了一个背包潜进了金店,包就那么大,他如果保证他背出来所有物品加起来的价值最大
  • 规范描述就是:有一个容量为 W 的背包,要用这个背包装下物品的价值最大,这些物品有两个属性:体积 w 和价值 v

一篇文章吃透背包问题!!!【动态规划】

背包问题分类:

最常见的背包问题有0-1背包完全背包多重背包分组背包这四种:

  • 按照每个物品的数量分类。
  • 其中最重要的是 0 - 1背包完全背包

一篇文章吃透背包问题!!!【动态规划】

0 - 1背包

问题描述:一个最多能背体积为 W背包n物品。第 i 件物品的体积是 weight[i] ,价值是 value[i]每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大

解题思路:

定义一个二维数组 dp 存储最大价值,其中 dp[i][j] 表示前 i 件物品体积不超过 j 的情况下能达到的最大价值。

设第 i 件物品体积为 w[i],价值为 v[i],根据第 i 件物品 是否添加 到背包中,可以分两种情况讨论:

  1. i 件物品 没添加 到背包,总体积不超过 j 的前 i 件物品的最大价值就是总体积不超过 j 的前 i -1 件物品的最大价值,dp[i][j] = dp[i-1][j]
  2. i 件物品 添加 到背包中,dp[i][j] = dp[i-1][j-w[i]] + v[i]

一篇文章吃透背包问题!!!【动态规划】

i 件物品可添加也可以不添加,取决于哪种情况下最大价值更大。因此,0 -1 背包的状态转移公式为:
d p [ i ] [ j ] = max ⁡ ( d p [ i − 1 ] [ j ] , d p [ i − 1 ] [ j − w [ i ] ] + v [ i ] ) d p[i][j]=\max (dp[i-1][j], dp[i-1][j-w[i]]+v[i]) dp[i][j]=max(dp[i1][j],dp[i1][jw[i]]+v[i])

万能统一代码:(Java、C++)

Java:

// W 为背包总体积
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
    int[][] dp = new int[N + 1][W + 1];
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= W; j++) {
            if (j >= weights[i - 1]) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}

C++ :

// W 为背包总体积
// N 为物品数量
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
int knapsack(int W, int N, vector<int> weights, vector<int> values) {
    vector<vector<int>> dp(N + 1, vector<int>(W + 1));
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= W; j++) {
            if (j >= weights[i - 1]) {
                dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}

空间优化

在程序实现时可以对 0 - 1 背包做优化。观察状态转移方程可以知道,前 i 件物品的状态仅与前 i - 1 件物品的状态有关,因此可以将 dp 定义为一维数组,其中 dp[j] 既可以表示 dp[i-1][j] 也可以表示 dp[i][j]。此时,
d p [ j ] = max ⁡ ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) d p[j]=\max (dp[j], dp[j-w[i]]+v[i]) dp[j]=max(dp[j],dp[jw[i]]+v[i])

因为要使用 dp[j-w[i]] 表示 dp[i-1][j-w[i]],因此不能先求 dp[i][j-w[i]],防止将 dp[i-1][j-w[i]] 覆盖。

也就是说要先计算 dp[i][j] 再计算 dp[i][j-w[i]],在程序实现时需要按 倒序 来循环求解。
Java:

public int knapsack(int W, int N, int[] weights, int[] values) {
    int[] dp = new int[W + 1];
    for (int i = 1; i <= N; i++) {
        for (int j = W; j >= 1; j--) {
            if (j >= weights[i - 1]) {
                dp[j] = Math.max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);
            }
        }
    }
    return dp[W];
}

C++:

int knapsack(int W, int N, vector<int> weights, vector<int> values) {
    vector<int> dp(W + 1);
    for (int i = 1; i <= N; i++) {
        for (int j = W; j >= 1; j--) {
            if (j >= weights[i - 1]) {
                dp[j] = Math.max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);
            }
        }
    }
    return dp[W];
}

完全背包

与 0 - 1 背包问题相似,但在完全背包问题中,物品的数量是无限的,可以选择任意多个相同的物品放入背包。

解题思路:

和 0 - 1 背包类似,我们可以定义一个二维数组 dp,其中 dp[i][j] 表示在前 i 个物品中,背包容量为 j 时能够获得的最大价值。

动态规划的状态转移公式如下:
d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − w [ i ] ] + v [ i ] ) dp[i][j] = max(dp[i-1][j], dp[i][j-w[i]] + v[i]) dp[i][j]=max(dp[i1][j],dp[i][jw[i]]+v[i])

  • 其中 w[i] 表示第 i 个物品的重量,v[i] 表示第 i 个物品的价值。
  • 第一项 dp[i-1][j] 表示 不选择i 个物品;
  • 第二项 dp[i][j-w[i]] + v[i] 表示 选择i 个物品(可能不止一个,和0-1背包相区别!)。

万能统一代码:(Java、C++)

Java:

// W 为背包总体积
// N 为物品的种类
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
public int knapsack(int W, int N, int[] weights, int[] values) {
    int[][] dp = new int[N + 1][W + 1];
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= W; j++) {
            if (j >= weights[i - 1]) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}

C++ :

// W 为背包总体积
// N 为物品的种类
// weights 数组存储 N 个物品的重量
// values 数组存储 N 个物品的价值
int knapsack(int W, int N, vector<int> weights, vector<int> values) {
    vector<vector<int>> dp(N + 1, vector<int>(W + 1));
    for (int i = 1; i <= N; i++) {
        for (int j = 1; j <= W; j++) {
            if (j >= weights[i - 1]) {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - weights[i - 1]] + values[i - 1]);
            } else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    return dp[N][W];
}

空间优化

  • 和 0 - 1 背包类似,可以优化空间复杂度为一维数组,使用 dp[j] 表示背包容量为 j 时的最大价值。状态转移公式为:
    d p [ j ] = m a x ( d p [ j ] , d p [ j − w [ i ] ] + v [ i ] ) dp[j] = max(dp[j], dp[j-w[i]] + v[i]) dp[j]=max(dp[j],dp[jw[i]]+v[i])

和0-1背包不同,在程序实现时按 正序 来循环求解。

Java:

public int knapsack(int W, int N, int[] weights, int[] values) {
    int[] dp = new int[W + 1];
    for (int i = 1; i <= N; i++) {
       for (int j = 1; j <= W; j++) {
            if (weights[i - 1] <= j) {
                dp[j] = Math.max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);
            }
        }
    }
    return dp[W];
}

C++:

int knapsack(int W, int N, vector<int> weights, vector<int> values) {
    vector<int> dp(W + 1);
    for (int i = 1; i <= N; i++) {
       for (int j = 1; j <= W; j++) {
            if (weights[i - 1] <= j) {
                dp[j] = max(dp[j], dp[j - weights[i - 1]] + values[i - 1]);
            }
        }
    }
    return dp[W];
}

无法使用贪心算法的解释

0-1 背包问题无法使用贪心算法来求解,也就是说不能按照先添加性价比最高的物品来达到最优,这是因为这种方式可能造成背包空间的浪费,从而无法达到最优

考虑下面的物品和一个容量为 5 的背包:

  • 如果先添加物品 0 再添加物品 1,那么只能存放的价值为 16,浪费了大小为 2 的空间。
  • 最优的方式是存放物品 1物品 2,价值为 22.

一篇文章吃透背包问题!!!【动态规划】

所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的;
只需记住动规是由前一个状态推导出来的,而贪心是局部直接选最优的。

LeetCode题目 —— 实战 !!!:

416. 分割等和子集
494. 目标和
474. 一和零
322. 零钱兑换
518. 零钱兑换 II
139.单词拆分
377. 组合总和 Ⅳ
1049. 最后一块石头的重量 II

注:仅供学习参考!文章来源地址https://www.toymoban.com/news/detail-449917.html

到了这里,关于一篇文章吃透背包问题!!!【动态规划】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 一篇文章搞定《Android权限问题(全版本)》

    文章内容如下: 如果你只是想快速的完成你Android权限申请的工作,那么直接上工具PermissionX 如果是想真正的了解Android的权限问题,那么建议你用15分钟通读一下本文。(可以不去实验,收藏以备后用) 首先了解Android版本和SDK的关系,帮助我们分辨后面的权限版本。 其次把最常

    2024年02月03日
    浏览(40)
  • 一篇文章搞定Android权限问题(全版本)

    文章内容如下: 如果你只是想快速的完成你Android权限申请的工作,那么直接上工具PermissionX 如果是想真正的了解Android的权限问题,那么建议你用15分钟通读一下本文。(可以不去实验,收藏以备后用) 首先了解Android版本和SDK的关系,帮助我们分辨后面的权限版本。 其次把最常

    2023年04月20日
    浏览(41)
  • 一篇文章彻底弄懂Golang私有仓库配置问题

    一般通过 go get 拉取的是公共仓库的代码(如: github.com中的代码),是不需要任务权限就能拉下来。但当我们配置的私有仓库一般都需要用户名密码来登录才能拉取代码,所以私有仓库主要是解决认证问题。 在早期版本的Go中,“go get”用于构建和安装包。现在,“go get”专门用

    2024年01月16日
    浏览(39)
  • 【跨域】一篇文章彻底解决跨域设置cookie问题!

    大家好我是雪人~~⛄ 之前做项目的时候发现后端传过来的 SetCookie 不能正常在浏览器中使用。 是因为谷歌浏览器新版本Chrome 80将Cookie的SameSite属性默认值由None变为Lax。 接下来带大家解决该问题。 我们可以看到Cookie有以下属性 Cookie属性 名称 :Cookie的name。 值 :Cookie的value。

    2024年02月02日
    浏览(36)
  • 一篇文章教你ctfd平台搭建&ctfd动态靶机创建&docker的使用&ctf动态flag的实现 来我这就够了!

    目录 一、ctfd的搭建 先换个源 开始安装docker 启动Docker服务并设置为开机启动 下载CTFd修改版 构建镜像 部署容器 二、开始部署一个ctfd赛题 现成的题库演示: 一个docker镜像: 选择dynamic_docker: 部署完很多很多的题目 点击开启,点击网址 三、怎么自己写一个ctf题目 👌好!首先

    2024年02月04日
    浏览(33)
  • AI辅写疑似度高风险怎么改:一篇文章为你解答七个关键问题

    大家好,今天来聊聊AI辅写疑似度高风险怎么改:一篇文章为你解答七个关键问题,希望能给大家提供一点参考。 以下是针对论文AI辅写率高的情况,提供一些修改建议和技巧,可以借助此类工具: 还有: AI辅写疑似度高风险怎么改:一篇文章为你解答七个关键问题 随着人工

    2024年02月20日
    浏览(30)
  • 动态规划-背包问题-完全背包

    对比01背包,完全背包中的每件物品有无数件。 也就是说,每件物品可以拿0,1,…,k,…件。 dp[i][j]表示前i种物品,体积为j时的最大价值 对于第i件物品: 不拿:dp[i][j]⇐dp[i-1][j] 拿一件:dp[i][j]⇐dp[i-1][j-w[i]]+v[i] 拿两件:dp[i][j]⇐dp[i-1][j-2w[i]]+2v[i] … 拿k件:dp[i]][j]⇐dp[i

    2024年04月08日
    浏览(37)
  • 动态规划之背包问题——完全背包

    算法相关数据结构总结: 序号 数据结构 文章 1 动态规划 动态规划之背包问题——01背包 动态规划之背包问题——完全背包 动态规划之打家劫舍系列问题 动态规划之股票买卖系列问题 动态规划之子序列问题 算法(Java)——动态规划 2 数组 算法分析之数组问题 3 链表 算法

    2024年02月03日
    浏览(30)
  • 完全背包&多重背包问题(动态规划)

    完全背包问题: 每个物品使用次数没有限制,与0-1背包的不同之处在于 遍历背包的顺序 是正序。 多重背包问题: 与完全背包的区别在于,每一种物品是有个数限制的,不能无限选择。这篇博客讲解的非常详细,可以参考学习: 多重背包问题---超详细讲解+优化(不懂你揍我

    2024年04月10日
    浏览(36)
  • 【动态规划专栏】-- 01 背包问题 -- 动态规划经典题型

    目录 背包问题概述 01 背包问题 01背包⭐⭐  【算法原理】 第一问 第二问 C++ 算法代码 复杂度分析 【空间优化 - 滚动数组】 C++ 算法代码 复杂度分析 分割等和子集⭐⭐ 【算法原理】  对于类01背包问题 C++ 算法代码  【空间优化 - 滚动数组】  C++ 算法代码 目标和⭐⭐ 【算

    2024年02月05日
    浏览(45)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包