IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 动态规划-01背包 -> 正文阅读

[数据结构与算法]动态规划-01背包

动态规划-01背包

动态规划中最为经典的题目必然就是背包问题, 而背包问题的基础就是01背包.

引用一个大佬梳理的背包图解

由上图可以看到,01背包其实就是背包选物品,一种物品只有一个,要么选,要么不选.

先通过一个简单的01背包问题来理解01背包问题.

01背包

有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

| 物品名称 | 价值 | 重量 |
| ----- |:----: |
| 物品0 | 1 | 10 |
| 物品1 | 2 | 15 |
| 物品2 | 51 | 20 |

根据我们上一张总结的动态规划步骤,

第一步:确定dp数组

dp[i][j]:从0到i件物品中选择放入背包容量为j的背包中的最大价值

第二步:递推公式

dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i])
按照题目的描述,一个物品要么选,要么不选,选的话只能选一次 物品i不选的情况为dp[i-1][j],物品i选择的情况是dp[i-1][j-weight[i]] + value[i];

第三步:dp数组初始化

物品0放入容量小于它重量的背包的value应该都是0,容量为0的背包放不下任何一件物品.
dp[0][i] = 0;
dp[j][0] = 0;

第四步:确定遍历顺序

遍历的顺序:从前到后遍历

第五步:举例推导dp数组

滚动数组

前面一篇中我们有提到滚动数组,作为优化空间复杂度.

01背包是否可以用滚动数组呢?

当然是可以的,我们可以看到01背包的遍历也是一层一层的往下的

所以公式 dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]) 好像可以简化成 dp[j] = max(dp[j], dp[j-weight[i]] + value[i]).

有没有感觉有什么不对?

是的, 如果按照二维数组的遍历顺序来执行这个一位数组,会导致被重复的抽取,即从小到大的遍历顺序时,

dp[j] = max(dp[j], dp[j-weight[i]] + value[i]) 等价于二维数组的 dp[i][j] = max(dp[i-1][j], dp[i][j-weight[i]] + value[i])

所以在一维数组的公式下,我们需要以背包容量倒序的顺序进行遍历.

代码实现

func backpack01(arr [][]int, cap int) int {
	if len(arr) == 0 {
		return 0
	}
	x := len(arr)
	dp := make([][]int, x)
	for k := range dp {
		dp[k] = make([]int, cap + 1)
	}
	for i := 0; i < x; i++  {
		dp[i][0] = 0
	}
	for j := 0; j <= cap; j++ {
		if arr[0][0] <= j {
			dp[0][j] = arr[0][1]
		} else {
			dp[0][j] = 0
		}
	}
	var tmp float64
	for i := 1; i < x; i++  {
		for j := 1; j <= cap; j++ {

			if j - arr[i][0] >= 0  {
				tmp = math.Max(float64(dp[i - 1][j]), float64(dp[i - 1][j - arr[i][0]] + arr[i][1]))
			} else {
				tmp = float64(dp[i - 1][j])
			}
			dp[i][j] = int(tmp)
		}
	}
	return dp[x - 1][cap]
}

扩展

416. 分割等和子集

给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

注意:
每个数组中的元素不会超过 100
数组的大小不会超过 200

示例 1:

输入: [1, 5, 11, 5]

输出: true

解释: 数组可以分割成 [1, 5, 5] 和 [11].

示例 2:

输入: [1, 2, 3, 5]

输出: false

解释: 数组不能分割成两个元素和相等的子集.

学习渠道: 微信公众号 代码随想录

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-09-10 11:06:45  更:2021-09-10 11:07:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/30 1:21:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码