算法学习笔记(8.3)-(0-1背包问题)

目录

最常见的0-1背包问题:

第一步:思考每轮的决策,定义状态,从而得到dp表

第二步:找出最优子结构,进而推导出状态转移方程

第三步:确定边界条件和状态转移顺序

方法一:暴力搜素

代码示例:

方法二:记忆化搜索

时间复杂度取决于子问题数量,也就是O(n*cap)。

实现代码如下:

方法三:动态规划

代码如下所示:

方法四:空间优化

代码示例

最常见的0-1背包问题:

Question:给定n个物品,第i个物品的重量为wgt[i]、价值为val[i],和一个容量为cap的背包。每个物品只能选择一次,问在限定背包的容量下能放入物品的最大价值。

观察图下所示,由于物品编号i从1开始计数,数组索引从0开始计数,因此物品i对应重量wgt[i]和价值val[i]。

我们可以将0-1背包问题看作一个由n轮决策组成的过程,对于每个物体都有不放入和放入两种决策,因此该问题满足决策树模型。

该问题的目标是求解“在限定背包容量下能放入物品的最大价值”,因此较大概率是一个动态规划的问题。

第一步:思考每轮的决策,定义状态,从而得到dp表

对于每个物品来说,不放入背包,背包容量不变;放入背包,背包容量减小。由此可得状态定义:当前物品编号i和剩余背包容量c,记作[i,c]。

状态[i,c]对应的子问题为:前i个物品在剩余容量为c的背包中的最大价值,记为dp[i,c]。

待求解的是dp[n,cap],因此需要一个尺寸为(n+1) * (cap+1)的二维dp表。

第二步:找出最优子结构,进而推导出状态转移方程

当我们做出物品i的决策后,剩余的是前i-1个物品的决策,可分为以下两种情况。

  1. 不放入物品i:背包容量不变,状态变化为[i-1,c].
  2. 放入物品i:背包容量减少wgt[i-1],价值增加val[i-1],状态变化为[i-1,c-wgt[i-1]]。

上述分析揭示了本题的最优子结构:最大价值dp[I,c]等于不放入物品i和放入物品i两种方案中价值更大的那一个。由此可以推导出状态转移方程为:

dp[i,c] = max(dp[i-1,c],dp[i-1,c-wgt[i-1] ] + val[i-1])

需要注意是,当前物品重量wgt[i-1]超过剩余背包容量c,则只能选择不放入背包。

第三步:确定边界条件和状态转移顺序

当无物品时或无背包剩余容量时最大价值为0,即首列dp[i,0]和首列dp[0,c]都等于0.

当前状态[i,c]从上方的状态[i-1,c]和左上方的状态[i-1,c-wgt[i-1]]转移而来,因此通过两层循环正序遍历整个dp表即可。

根据以上的分析,采取三种方法顺序进行实现暴力搜索,记忆化搜索,动态规划解法。

方法一:暴力搜素

搜索代码需要包含的要素:

  1. 递归参数:状态[i,c]
  2. 返回值:子问题的解dp[i,c]
  3. 终止条件:当物品编号越界I = 0 或背包容量为0时,终止递归并返回价值0.
  4. 剪枝:当前物品重量超出背包剩余容量时,则只能选择不放入背包。
代码示例:
# python 代码示例def knapsack_dfs(wgt, val, i, c) :if i == 0 or c == 0 :return 0if wgt[i - 1] > c :return knapsack(wgt, val, i - 1, c)nohave = knapsack_dfs(wgt, val, i - 1, c)have = knapsack_dfs(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1]return max(nohave, have)
// C++代码示例int knapsackDFS(vector<int> &wgt, vector<int> &val, int i, int c) :if (i == 0 || c == 0){return 0 ;}if (wgt[i - 1] > c){return knapsackDFS(wgt, val, i - 1, c) ;}int nohave = knapsackDFS(wgt, val, i - 1, c) ;int have = knapsackDFS(wgt, val, i - 1, c - wgt[i - 1]) + val[i - 1] ;return max(nohave, have) ;

如图所示:由于每个物品都会产生选或者不选两条搜索分支,因此时间复杂度为O(2^n)。

观察递归树,容易发现其中存在重叠子问题,例如dp[1,10]。而当物品较多,背包容量较大,尤其是相同重量的物品较多时,重叠子问题的数量将会大幅度增多。

方法二:记忆化搜索

为了保证重叠子问题只被计算一次,我们借助记忆列表mem来记录子问题的解,其中menm[i][c]对应dp[i][c]。

时间复杂度取决于子问题数量,也就是O(n*cap)。
实现代码如下:
# python 代码示例
def knapsack_dfs_mem(wgt, val, i, c, mem) :if i == 0 or c == 0 :return 0if mem[i][c] != -1 :return mem[i][c]if wgt[i - 1] > c :return knapsack_dfs_mem(wgt, val, i - 1, c, mem)noput = knapsack_dfs_mem(wgt, val, i - 1, c, mem)yesput = knapsack_dfs_mem(wgt, val, i - 1, c - wgt[i - 1], mem) + val[i - 1]mem[i][c] = max(noput, yesput)return mem[i][c]
// c++ 代码示例
int knapSackDFSMem(vector<int> &wgt, vector<int> &val, int i, int c, vector<int> &mem)
{if (i == 0 || c == 0){return 0 ;}if (mem[i][c] != 0){return mem[i][c] ;}if (wgt[i - 1] > c){return knapSackDFSMem(wgt, val, i - 1, c, mem) ;}int noput = knapSackDFSMem(wgt, val, i - 1, c, mem) ;int yesput = knapSackDFSMem(wgt, val, i - 1, c - wgt[i - 1], mem) + val[i - 1];mem[i][c] = max(noput, yesput) ;return mem[i][c] ;
}

方法三:动态规划

动态规划实质是就是在状态转移的过程中填充dp表的过程,

代码如下所示:
# python 代码示例
def knapsack_dp(wgt, val, cap) :n = len(wgt)dp = [ [0] * (cap + 1) for _ in range(n + 1)]for i in range(1, n + 1) :for c in range(1, cap + 1) :if wgt[i - 1] > c :dp[i][c] = dp[i - 1][c]else :dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1]) return dp[n][cap] 
// c++ 代码示例
int knapSackDP(vector<int> &wgt, vector<int> &val, int cap)
{ int n = wgt.size() ;vector<vector<int>> dp(n + 1, vector<int>(cap + 1, 0)) ;for (int i = 1 ; i <= n ; i++){for (int c = 1 ; c <= cap ; c++){if (wgt[i - 1] > c){dp[i][c] = dp[i - 1][c] ;}else{dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + val[i - 1])  ;}}}return dp[n][cap] ;
}

时间复杂度和空间复杂度都是由数组dp所决定的,即O(n*cap)。

如图所示:

方法四:空间优化

由于每个状态都至于其上一行有关系,因此我们可以使用两个数组进行滚动前进,将复杂度从O(n^2)降低为O(n)。

进一步思考,我们能否仅用一个数组实现空间优化?观察可知,每个状态都是有正上方或左上方的格子的状态转移而来。假设只有一个数组,当开始遍历第i行时,该数组存储仍然是第i-1行的状态。

  1. 如果采取正序遍历,那么遍历到dp[i,j]时,左上方的dp[i-1,1]~dp[i-1,j-1]值可能已经覆盖了,因此无法得到状态转移的正确结果。
  2. 如果采取倒序遍历,则不会发生覆盖问题,状态转移可以正确的进行。

代码示例:
def knap_sack_dp_comp(wgt, val, cap) :n = len(wgt)dp = [0] * (cap + 1)for i in range(1, n + 1) :for c in range(cap, 0 ,-1) :if wgt[i - 1] > c :dp[c] = dp[c]else :dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1])return dp[cap]    
// c++ 代码示例
int kanpSackDPComp(vector<int> &wgt, vector<int> &val, int cap)
{int n = wgt.size() ;vector<int> dp(cap + 1, 0) ;for (int i = 1 ; i <= n ; i++){for (int c = cap; c > 0 ; c--){if (wgt[i - 1] > c){dp[c] = dp[c] ;}else{dp[c] = max(dp[c], dp[c - wgt[i - 1]] + val[i - 1]) ;}}}return dp[cap] ;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/45050.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MFC之对话框--线宽/线型/颜色

文章目录 线宽输入实现优化无法记录上一次线粗问题 线宽滑动实现实现选择线类型实现颜色选择总结 线宽输入实现 优化无法记录上一次线粗问题 线宽滑动实现 实现选择线类型 实现颜色选择 总结 1。创建新窗口&#xff08;dialog)会创建一个新的类&#xff0c;在类中实现窗口中的…

vue中父子传递属性值

1、父传子属性值 自定义图库组件 在add.vue中应用tuku组件并给默认值 效果 2、 子传父&#xff0c;逆向赋值 add.vue和第一问中一样 修改tuku组件&#xff0c;传值给add.vue 3、多个传递 效果&#xff1a; 点击两个修改按钮后 4、使用defineModel简化父子传值 其他代码跟…

【postgresql】时间函数和操作符

日期/时间操作符 加减操作符&#xff1a; 和 - 可以用于日期、时间、时间戳和时间间隔的加减操作。 SELECT 2024-01-01::date INTERVAL 1 day as "date"; ; -- 结果&#xff1a;2024-01-02SELECT 2024-01-01 12:00:00::timestamp - INTERVAL 2 hours as "…

CSS上下悬浮特效

要实现一个上下悬浮的特效&#xff0c;可以使用CSS的keyframes规则和动画属性。以下是一个简单的示例&#xff1a; 代码示例 /* 定义一个名为floating的动画 */ keyframes floating {0% {transform: translateY(0); /* 初始位置 */}50% {transform: translateY(-4px); /* 向上…

KALI使用MSF攻击安卓设备

这期是kali使用MSF进行安卓渗透的保姆级别教程&#xff0c;话不多说&#xff0c;直接开始。 准备材料&#xff1a; 1.装有kali的实体机或虚拟机&#xff08;这里用实体机进行演示&#xff09; 2.一台安卓10.0以下的手机 打开kali&#xff0c;先用ifconfig查看自己的kali IP地址…

Python3极简教程(一小时学完)下

目录 PEP8 代码风格指南 知识点 介绍 愚蠢的一致性就像没脑子的妖怪 代码排版 缩进 制表符还是空格 每行最大长度 空行 源文件编码 导入包 字符串引号 表达式和语句中的空格 不能忍受的情况 其他建议 注释 块注释 行内注释 文档字符串 版本注记 命名约定 …

[BJDCTF2020]EasySearch1

知识点&#xff1a; 1.swp泄露 2.md5碰撞 3.PHP代码审计 4.SSI代码执行漏洞 // Apache SSI 远程命令执行漏洞复现 看着像sql注入&#xff0c;不过注入无果&#xff0c;扫一下目录试试~ 发现是swp泄露. SWP文件泄露漏洞是指在使用 Vim编辑器 编辑一个文件时&#xff0c;Vim会在…

OpenCV漫水填充函数floodFill函数的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 功能描述 ffloodFill函数是OpenCV库中用于图像处理的一个功能&#xff0c;它用于填充与种子点颜色相近的连通区域。这个函数在很多场景下都非常有用&#x…

MUR2060CTR-ASEMI无人机专用MUR2060CTR

编辑&#xff1a;ll MUR2060CTR-ASEMI无人机专用MUR2060CTR 型号&#xff1a;MUR2060CTR 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220 批号&#xff1a;最新 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;20A 最大循环峰值反向电压&#xff08;VRRM&#…

【数据结构】线性表----队列详解

1. 队列的基本概念 话不多说&#xff0c;直接开始&#xff01; 队列是一种线性数据结构&#xff0c;同栈类似但又不同&#xff0c;遵循先进先出&#xff08;FIFO, First In First Out&#xff09;的原则。换句话说&#xff0c;最先进入队列的元素会最先被移除。这样的特点使得…

小白学python(第七天)

哈哈&#xff0c;这个系列的文章也有一段时间没更新&#xff0c;主要是最近在忙c嘎嘎&#xff0c;不过没事接下来会优先更python啦&#xff0c;那么我们先进入正题吧 函数的定义及调用 函数定义 格式&#xff1a;def 函数名&#xff08;形参列表&#xff09;&#xff1a; 语…

QTabWidget、QListWidget、QStackedWidget

The QTabWidget class provides a stack of tabbed widgets. More... The QListWidget class provides an item-based list widget. More... QStringList strlist;strlist<<"系统"<<"外观"<<"截图"<<"贴图"…

Java的高级特性

类的继承 继承是从已有的类中派生出新的类&#xff0c;新的类能拥有已有类的属性和行为&#xff0c;并且可以拓展新的属性和行为 public class 子类 extends 父类{子类类体 } 优点 代码的复用 提高编码效率 易于维护 使类与类产生关联&#xff0c;是多态的前提 缺点 类缺乏独…

c/c++ 打印调用栈

打印调用栈可以在程序出现死机的时候&#xff08;如出现 SIGABRT、SIGSEGV等一些信号错误&#xff09;是很有用的信息&#xff0c;有可能就不需要 core file 来协助排查问题了。通过 man backtrace 可以得到一个例子的源码&#xff1a; #define SIZE 100 static void backTrac…

【机器学习-00】机器学习是什么?

在科技飞速发展的今天&#xff0c;机器学习已成为一个热门话题&#xff0c;广泛应用于各个行业和领域。那么&#xff0c;机器学习到底是什么&#xff1f;它又是如何工作的&#xff1f;本文将深入探讨机器学习的定义、原理及其在各领域的应用&#xff0c;带领读者走进这个神秘而…

RedisTemplate 中序列化方式辨析

在Spring Data Redis中&#xff0c;RedisTemplate 是操作Redis的核心类&#xff0c;它提供了丰富的API来与Redis进行交互。由于Redis是一个键值存储系统&#xff0c;它存储的是字节序列&#xff0c;因此在使用RedisTemplate时&#xff0c;需要指定键&#xff08;Key&#xff09…

Netgear WN604 downloadFile.php 信息泄露漏洞复现(CVE-2024-6646)

0x01 产品简介 NETGEAR WN604是一款由NETGEAR(网件)公司生产的无线接入器(或无线路由器)提供Wi-Fi保护协议(WPA2-PSK, WPA-PSK),以及有线等效加密(WEP)64位、128位和152位支持,保障网络安全。同时支持MAC地址认证、802.1x RADIUS以及EAP TLS、TTLS、PEAP等安全机制,…

Flat Ads:金融APP海外广告投放素材的优化指南

在当今全球化的数字营销环境中,金融APP的海外营销推广已成为众多金融机构与开发者最为关注的环节之一。面对不同地域、文化及用户习惯的挑战,如何优化广告素材,以吸引目标受众的注意并促成有效转化,成为了广告主们亟待解决的问题。 作为领先的全球化营销推广平台,Flat Ads凭借…

超简易高效的 AI绘图工具—与sd-webui一致界面,6G显存最高提升75%出图速率!(附安装包)

大家好&#xff0c;我是灵魂画师向阳 今天给大家分享一个基于Stable Diffusion WebUI 构建的AI绘图工具—sd-webui-forge&#xff0c;该工具的目标在于简化插件开发&#xff0c;优化资源管理&#xff0c;加速推理。 Forge承诺永远不会对Stable Diffusion WebUI用户界面添加不…

获奖案例回顾|基于卫星遥感和无人机的水稻全流程风险减量项目

引言 在现代农业保险领域&#xff0c;技术创新是推动行业进步的关键。珈和科技与太平财险的合作&#xff0c;旨在利用先进的卫星遥感和无人机技术&#xff0c;解决传统农业保险面临的诸多挑战&#xff0c;从而提升保险效率和服务质量。本次分享的项目案例获得了《金融电子化》…