动态规划——01背包问题

目录

零、背包问题

一、01背包

二、分割等和子集

三、目标和

四、最后一块石头的重量II


零、背包问题

背包问题(Knapsack problem)是一种组合优化的NP完全问题。

问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量(或体积)内,我们如何选择,才能使得物品的总价格最高。

其中的限制条件有:物品价值,背包容量大小,背包是要求必装满还是不必装满,每种物品的数量。

一、01背包

01背包可以描述为:有n件物品和一个最多能背体积为 v 的背包。第 i 件物品的体积是 v[i],得到的价值是value[i] 。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。

在01背包问题中,对于每件物品,只有两种状态:选或者不选。

题目

01背包

题目解析 

第一问的所有情况就是上图蓝色方框的内容, 第二问的所有情况就是上图红色方框的内容。

解决第一问 

第一步:确定状态表示

dp[i][j]:表示从前 i 个物品中挑选,总体积不超过 j 的所有选法中,能挑选出来的物品价值最大值。

第二步:推出状态转移方程

前面我们已经说明了,01背包中的每件物品只有一种,所以对于每件物品来说,只有选或者不选两种状态。根据最后一步的状况,分情况讨论,即要么选择 i 号物品,要么不选 i 号物品。

如果要选 i 物品,在所有选法中,最后一个被选择的一定是 i 物品,那么总价值要加上w[i]表示 i 号物品的价值。

接下来只要知道选到  i - 1 号物品时的最大价值就行了,但是此时的体积就不再是 j 了。因为挑到 i 号物品的时候总体积要求不超过 j ,那选了 i 号物品,然后从1 ~ i - 1物品选,要剩下一点体积能让我挑选 i 号物品。

所以体积需要剩余 j - v[i]。也就是说选了 i 号物品,接下来去1 ~ i - 1物品选总体积不能超过j - v[i]。

并且,前一个状态的最大价值,正好在 dp[i-1][j-v[i]]里存着。

注:j - v[i] 不一定存在。比如说挑选到 i 号物品的时候总体积要求不超过3,但是第 i 号物品的体积是6,这要求背包剩余空间是-3,是一个负数,那怎么也装不下。那选 i 号物品这种情况就相当于不存在。如果想选 i 号物品这种情况存在,那 j - v[i] >= 0。 

第三步:初始化dp表

为了方便,我们规定物品的编号是从1开始的。所以为了填写下标关系一一对应,我们给dp表多加一行和一列。

第一行 i 等于0,表示从0号物品中选,但是我们规定物品编号是从1开始的。第一行表示没有物品,如果没有物品,想凑成容量不超过 j 根本不可能做到,所以第一行值都为0。

第一列表示容量不超过0,那么不选的话容量就不会超过0了。

最后的返回值就是dp[n][V]。 

解决第二问 

第一步:确定状态表示

dp[i][j]: 表示从前 i 个物品中挑选,总体积恰好为 j 的所有选法中,能挑选出来的物品价值最大值。

第二步:推出状态转移方程

该状态转移方程和第一问的一模一样,但是需要注意一些限制条件。

dp[i][j]表示从前 i 个物品中挑选,总体积恰好为 j 的所有选法中,能挑选出来的物品价值最大值。该状态表示要求,当我们选到第 i 个物品的时候,体积要恰好为 j。但是我们不一定就能选到使其体积恰好为j。那就说明这种状态是不存在的。

为了处理这种情况,我们规定,dp[i][j] = -1 表示情况不存在。

所以我们在用每一个状态之前都要判断状态存不存在,然后才能使用。比如选 i 物品,要判断 dp[i-1][j-v[i]] != -1,然后再使用这个状态。

第三步:初始化dp表

第一列(下标从0开始)表示使体积恰好为0,什么也不选体积就恰好是0,价值也是0。

第一行(下标从1开始)表示没有物品,但是想凑成体积恰好等于 j 的情况,这种状态是根本不存在的,所以值为-1。

解题代码:

#include <algorithm>
#include <iostream>
#include <vector>using namespace std;int main()
{int n, V;cin >> n >> V;vector<int> v(n+1);vector<int> w(n+1);for(int i = 1; i <= n; i++)cin >> v[i] >> w[i];vector<vector<int>> dp1(n+1, vector<int>(V+1));for(int i = 1; i <= n; i++){for(int j = 1; j <= V; j++){dp1[i][j] = dp1[i-1][j];if(j-v[i] >= 0)dp1[i][j] = max(dp1[i][j], dp1[i-1][j-v[i]] + w[i]);}}vector<vector<int>> dp2(n+1, vector<int>(V+1));for(int i = 1; i <= V; i++)dp2[0][i] = -1;for(int i = 1; i <= n; i++){for(int j = 1; j <= V; j++){dp2[i][j] = dp2[i-1][j];if(j-v[i] >= 0 && dp2[i-1][j-v[i]] != -1)dp2[i][j] = max(dp2[i][j], dp2[i-1][j-v[i]] + w[i]);}}cout << dp1[n][V] << endl;cout << (dp2[n][V] == -1 ? 0 : dp2[n][V]) << endl;return 0;
}

 


二、分割等和子集

分割等和子集

题目解析 

题目要求将数组分成元素和相等的两个部分,而数组的元素总和是固定的,那就说明这两个部分的值都是数组元素总和的一半。

那么,这个问题就转化成了,从数组中选取一些元素,看能不能使选出来的元素和恰好为数组总和的一半。 每个元素只有两种状态,选或者不选,并且每个元素是只能选一次的。这不就是01背包问题吗。下面我们就用解决01背包问题的方法来解决这道题。

解题

第一步:确定状态表示

dp[i][j]:表示从数组的前 i 个元素中挑选元素,能不能使其总和恰好为 j,能就是true,不能就是false。

第二步:推出状态转移方程

第三步:初始化dp表

第一列(下标从0开始)表示使元素和恰好为0,什么也不选和就恰好是0,所以是true。

第一行(下标从1开始)表示没有元素,但是想凑成和恰好等于 j 的情况,这种状态是根本不存在的,所以值为false。

最后的返回值就是dp[m][sum/2]。 

解题代码:

class Solution 
{
public:bool canPartition(vector<int>& nums) {int m = nums.size();int sum = 0;for(auto x : nums)sum += x;if(sum % 2 != 0)return false;int target = sum / 2;vector<vector<bool>> dp(m+1, vector<bool>(target+1));for(int i = 0; i <= m; i++)dp[i][0] = true;for(int i = 1; i <= m; i++){for(int j = 1; j <= target; j++){dp[i][j] = dp[i-1][j];if(j >= nums[i-1])dp[i][j] = dp[i][j] || dp[i-1][j-nums[i-1]];}}return dp[m][target];}
};


三、目标和

目标和

题目解析

题目要求给数组的元素前面添上正号或者负号,那就相当于把一个元素变成正数或者负数。

所以,我们可以将元素分成两个部分,一部分是正数,一部分是负数。那么我们可以推出如下的结果。

推出的结果是 a = (target + sum)/ 2。

于是,问题就转化成了:只要在整个数组里挑选一些数让这些数的和等于a即可,然后问一共有多少种挑法。  

数组的每个数都有选或者不选两种状态。我们的目的是让选择的数的和等于a。这就是01背包问题里面把背包恰好装满的问题。

解题 

第一步:确定状态表示

dp[i][j]:表示从数组的前 i 个元素中选,使总和正好等于 j,一共有多少种选法。

第二步:推出状态转移方程

第三步:初始化dp表

第一行表示数组没有元素,如果数组里面没有元素,只要不选,就可以凑出和为0的情况,第一个位置为1,但是无法凑出和为1、2…的情况。所以第一行剩下都为0。

我们初始化的目的是为了在填表的时候不会发生越界访问。对于第一列剩下的位置,表示和恰好为0。因为数组元素可能是0,选 i 位置元素的时候,会判断是否 j >= nums[i],第一列的 j 都是0,要符合判断的话,只能是nums[i] == 0,nums[i] > 0 的话根本就不会进入 “选i位置元素” 的情况,所以绝对不会越界访问。

所以第一列剩下的位置可以放在填写dp表时进行填写。

解题代码:

class Solution 
{
public:int findTargetSumWays(vector<int>& nums, int target) {int sum = 0;for(auto x : nums)sum += x;int m = nums.size();int num = (target + sum) / 2;if(num < 0 || (sum + target) % 2)return 0;vector<vector<int>> dp(m+1, vector<int>(num+1));dp[0][0] = 1;for(int i = 1; i <= m; i++){for(int j = 0; j <= num; j++){dp[i][j] = dp[i-1][j];if(j >= nums[i-1])dp[i][j] += dp[i-1][j-nums[i-1]];}}return dp[m][num];}
};

 


四、最后一块石头的重量II

最后一块石头的重量II

题目解析

这道题如果直接用动态规划去做是很难的。状态表示定义不出来。所以我们先分析这道题看能不能把这道题转换一下。 

我们先模拟一下选择过程:

最后,数组中剩下的元素就是我们想要的结果。

我们发现,这和上一道题目标和好像有些像, 最后的结果也是在元素前面添上正号或者负号而得到的,那就相当于把一个元素变成正数或者负数。这样数组就会只剩下一个元素了。

这道题是让我们求剩下的石头的最小重量,所以这道题我们就可以转换成:在数组中选择一些数,让这些数的和尽可能的接近 sum / 2。只要能求出a就能求出b,然后让它俩相减就可以了。

数组里面所有的数都面临选或者不选两种情况,并且每个位置的元素只有一个,这就是典型的01背包问题。

解题 

第一步:确定状态表示

dp[i][j]:表示从前 i 个数中选,使其总和不超过 j,此时的最大和。

第二步:推出状态转移方程

第三步:初始化dp表

第一列不用初始化。

第一行表示数组为0的情况,也就是没有石头,要想凑成总和不超过j,当 j 是 0 的时候不选就可以了此时最大和为0。但当 j 是1、2…,根本凑不成,此时最大和还是0。

解题代码:

class Solution 
{
public:int lastStoneWeightII(vector<int>& stones) {int m = stones.size();int sum = 0;for(auto x : stones)sum += x;int target = sum / 2;vector<vector<int>> dp(m+1, vector<int>(target+1));for(int i = 1; i <= m; i++){for(int j = 0; j <= target; j++){dp[i][j] = dp[i-1][j];if(j >= stones[i-1])dp[i][j] = max(dp[i][j], dp[i-1][j-stones[i-1]] + stones[i-1]);}}return sum - 2 * dp[m][target];}
};

 

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

30.2 不得不谈的lsm:分层结构和lsm数据结构

本节重点介绍 : LSM树核心特点LSM树的核心结构 MemTableImmutable MemTableSSTable LSM树的Compact策略 size-tiered 策略leveled策略 LSM树(Log-Structured-Merge-Tree) LSM树的名字往往会给初识者一个错误的印象&#xff0c;事实上&#xff0c;LSM树并不像B树、红黑树一样…

宏观经济学笔记

【拯救者】宏观经济学速成 国民生产总值GNP: GNP 衡量一国(地区)成员在一定时期内运用生产要素所生产的全部最终产品和服务的市场价值。凡是本国国民所 创造的收入&#xff0c;不管生产要素是否在国内&#xff0c;都计入本国GNP中。 GDP本国居民在本国创造的价值外国居民在本国…

模块二:central cache实现

一、central cache介绍 结构也是一个哈希桶&#xff0c;大小划分和 thread cache哈希桶一样&#xff0c;区别在于挂的不是自由链表而是 span 链表&#xff0c;里面连接了许多 span 二、span介绍 1、实现思路 span 就是 central cache 向 page cache 申请的大块内存&#xff…

D-FINE:在DETRs模型中将回归任务重新定义为细粒度分布优化

晚上回家看到一篇新颖的研究内容&#xff0c; 也是目标检测相关的《D-FINE: REDEFINE REGRESSION TASK IN DETRS AS FINE-GRAINED DISTRIBUTION REFINEMENT》 &#xff0c;原文地址在这里&#xff0c;如下所示&#xff1a; 如果想进一步了解相关的研究工作建议移步阅读原英文论…

数据结构 ——— 链式二叉树oj题:单值二叉树

目录 题目要求 手搓一个单值二叉树 代码实现 题目要求 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false 手搓一个单值二叉树 代码演示&#xff1a; // 数据类…

使用Windbg排查C++软件安装包安装时被安全防护软件拦截导致安装线程堵塞卡住的问题

目录 1、问题描述 2、初步分析 3、将Windbg附加到安装包进程上进行分析 4、在Windbg中查看相关变量的值&#xff0c;并设置断点进行动态调试 4.1、在Windbg中查看相关变量的值 4.2、在Windbg中使用bp命令设置断点进行动态调试 5、腾讯电脑管家已经退出&#xff0c;但其…

一键直达Windows11精简版下载地址:附快速安装教程!

许多用户想知道Windows11精简版下载地址在哪里&#xff1f;这里系统之家小编将给大家分享最新的Windows11精简版系统下载地址&#xff0c;方便大家下载与安装。该版本系统删除大量不必要的组件和功能&#xff0c;让系统运作速度变得更快更流畅&#xff0c;但没有过度精简&#…

Mesh网格

Mesh(网格) 定义&#xff1a;Mesh 是一个包含顶点、三角形、顶点法线、UV坐标、颜色和骨骼权重等数据的对象。它定义了3D模型的几何形状。 功能&#xff1a; 顶点&#xff08;Vertices&#xff09;&#xff1a;构成3D模型的点。 三角形&#xff08;Triangles&#xff09;&…

【机器学习】28. 强化学习(Bellman, Q-learning, DQN, 优先级经验回放)

强化学习 定义强化学习的核心要素马尔可夫决策过程价值函数Bellman 方程Q Learning深度Q学习算法 &#xff08;DQN&#xff09;DQN 的核心思想DQN 的工作流程经验回放&#xff1a;&#xff08;随机抽样&#xff09;目标网络&#xff1a;损失函数 优先级经验回放&#xff08;Pri…

大数据-217 Prometheus 安装配置 启动服务 监控服务

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

利用RANSAC算法拟合平面并生成包围框的点云处理方法,点云聚类、质心坐标、倾斜角度、点云最小外接矩形

该代码用于分析和处理点云数据&#xff0c;通过对点云数据进行裁剪、平面拟合和生成包围框来提取特定区域的特征并发布结果。主要使用了RANSAC算法来识别并拟合平面&#xff0c;从而提取平面的法向量&#xff0c;接着根据该平面计算出该区域的最小矩形包围框&#xff08;Boundi…

算法妙妙屋-------1.递归的深邃回响:二叉树的奇妙剪枝

大佬们好呀&#xff0c;这一次讲解的是二叉树的深度搜索&#xff0c;大佬们请阅 1.前言 ⼆叉树中的深搜&#xff08;介绍&#xff09; 深度优先遍历&#xff08;DFS&#xff0c;全称为DepthFirstTraversal&#xff09;&#xff0c;是我们树或者图这样的数据结构中常⽤的⼀种…

深入解析DHCP带来了什么功能,服务器回应到底是用广播还是单播呢?

前言 不知道大家在看到这个图的时候第一时间想到的是什么&#xff0c;【好复杂】【看不懂】【终端数好多】&#xff0c;这里不看整体的结构怎么样&#xff0c;来看看终端数量都非常的多&#xff0c;终端要与网络中进行通信&#xff0c;势必需要IP地址&#xff0c;从最开始学习到…

<项目代码>YOLOv8 棉花识别<目标检测>

YOLOv8是一种单阶段&#xff08;one-stage&#xff09;检测算法&#xff0c;它将目标检测问题转化为一个回归问题&#xff0c;能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法&#xff08;如Faster R-CNN&#xff09;&#xff0c;YOLOv8具有更高的…

知乎日报前三周总结

目录 前言 首页 网络请求 上拉加载 详情页 加载WebView 左右滑动 主页与详情页同步更新 总结 前言 在这几周进行了知乎日报的仿写&#xff0c;这篇博客来总结一下前三周仿写的内容 首页 首页的界面如图所示&#xff0c;其实就是一个导航栏和一个数据视图组成的&#…

小白快速上手 labelimg:新手图像标注详解教程

前言 本教程主要面向初次使用 labelimg 的新手&#xff0c;详细介绍了如何在 Windows 上通过 Anaconda 创建和配置环境&#xff0c;并使用 labelimg 进行图像标注。 1. 准备工作 在开始本教程之前&#xff0c;确保已经安装了 Anaconda。可以参考我之前的教程了解 Anaconda 的…

【算法】【优选算法】二分查找算法(上)

目录 一、二分查找简介1.1 朴素二分模板1.2 查找区间左端点模版1.3 查找区间右端点模版 二、leetcode 704.⼆分查找2.1 二分查找2.2 暴力枚举 三、Leetcode 34.在排序数组中查找元素的第⼀个和最后⼀个位置3.1 二分查找3.2 暴力枚举 四、35.搜索插⼊位置4.1 二分查找4.2 暴力枚…

自己构建ARM平台DM8镜像

&#xff1f;&#xff1f;&#xff1f; 为什么不使用官方提供的docker版本&#xff0c;测试有问题&#xff0c;分析函数不能使用&#xff0c;报错。 自己构建ARM平台的dm8镜像&#xff0c;参考 https://gitee.com/xlongfu/dm-docker/tree/master&#xff0c;发现一些问题 首先…

Linux之实战命令73:at应用实例(一百零七)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…