0/1背包问题总结

文章目录

  • 🍇什么是0/1背包问题?
  • 🍈例题
    • 🍉1.分割等和子集
    • 🍉2.目标和
    • 🍉3.最后一块石头的重量Ⅱ
  • 🍊总结

在这里插入图片描述
博客主页:lyyyyrics

🍇什么是0/1背包问题?

0/1背包问题是一个经典的组合优化问题,其描述如下:

假设有一个背包,它能够承载一定的重量。现在有一组物品,每个物品有各自的重量和价值。我们的目标是在不超过背包承载重量的前提下,选择一些物品放入背包中,使得背包中物品的总价值最大化。

具体来说,假设有 n n n个物品,其重量分别为 w 1 , w 2 , . . . , w n w_1, w_2, ..., w_n w1,w2,...,wn,对应的价值分别为 v 1 , v 2 , . . . , v n v_1, v_2, ..., v_n v1,v2,...,vn。背包的承载重量为 W W W。我们需要在这 n n n个物品中选择一些放入背包中,使得这些物品的总重量不超过 W W W,且总价值最大化。

数学公式可以表示为:

Maximize ∑ i = 1 n v i x i subject to ∑ i = 1 n w i x i ≤ W x i ∈ { 0 , 1 } , i = 1 , 2 , . . . , n \begin{align*} \text{Maximize} \quad & \sum_{i=1}^{n} v_ix_i \\ \text{subject to} \quad & \sum_{i=1}^{n} w_ix_i \leq W \\ & x_i \in \{0, 1\}, \quad i=1,2,...,n \end{align*} Maximizesubject toi=1nvixii=1nwixiWxi{0,1},i=1,2,...,n

其中, x i x_i xi表示第 i i i个物品是否放入背包中。若 x i = 1 x_i=1 xi=1,表示放入;若 x i = 0 x_i=0 xi=0,表示不放入。

🍈例题

🍉1.分割等和子集

题目:

在这里插入图片描述

样例输出和输入:

在这里插入图片描述

根据描述,这道题就是让我们求一个数组是否能将其分成两块 ,然后这两块是相等的,如果能返回true,如果不能则返回false。
算法原理:
首先我们注意到,这道题要将数组分成两个部分,这两个部分是否相等,我们可以转化为分成一个部分,这个部分是数组总和的一半?很显然是可以的,这样转换之后其实就已经是背包问题了,这个数组中的数就是物品,数组中的数就代表每个物品的价值,然后数组中的数的总和的一半就是这个背包的容量,问题就 转化为,我们是否可以从数组中挑出一些物品,使得物品的体积恰好能把这个 背包塞满?
状态表示:dp[i][j]表示前i个数中的所有的选法中,是否存在和为j的选法,如果存在则是true,如果不存在则就是false。
状态转移方程:状态转移方程还是考虑i位置和j位置。
在这里插入图片描述
如果能选的话,应该是选和不选中有一个满足就够了,所以这里应该还要取一个||,dp[i][j]=dp[i-1][j]||dp[i-1][j-nums[i]]
初始化:在这里插入图片描述
首先看第一行,从1开始,从0个数中选,和为1的,这种情况是不存在的,所以应该是false,后面的从0个数中选的都是false,,我们来看第一列,从0个数中选和为0,那就是不选,所以为true,从1个数中选和为0,可以不选,也是true,所以我们只需要 初始化第一列,初始化为true。
代码:

class Solution {
public:
    bool canPartition(vector<int>& nums)
    {
        int n = nums.size();
        int sum = 0;
        //求和
        for (auto e : nums)sum += e;
        //如果和是奇数的话直接返回
        if (sum % 2 == 1)return false;
        //先定义一个aim表示和的一半
        int  aim = sum / 2;
        //创建dp表
        vector<vector<bool>> dp(n + 1, vector<bool>(aim + 1));
        //初始化dp表的第一列为true,因为如果和为零的话是有可能的。
        //如果什么都不选的话,当前和就是0
        for (int i = 0;i <= n;i++)
        {
            dp[i][0] = true;
        }
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= aim;j++)
            {
                //不选的话就是前一个状态的bool值
                dp[i][j] = dp[i - 1][j];
                //如果能选的话,只要有一种满足就表示满足
                if (j >= nums[i - 1])dp[i][j] = dp[i - 1][j] || dp[i - 1][j - nums[i - 1]];
            }
        }
        //返回和为aim的状态
        return dp[n][aim];
    }
};

运行结果:
在这里插入图片描述

🍉2.目标和

题目:

在这里插入图片描述

样例输出和输入:

在这里插入图片描述

这道题是给定一个数组,我们可以将数组中数 的符号我们可以随便改,最后串成一个加减法的式子,然后这个式子的值为target即可,这道题当然不是让我们返回true或者false,而是让我们返回方法的总数。
算法原理:
这道题其实我们可以将当中的数划分为两类:
在这里插入图片描述
我们将b规定为负数的绝对值。
所以最后可以得出:在这里插入图片描述
所以我们只需要看这个数组中是否组合能使得这个组合最后的和是a即可。
状态表示:dp[i][j]表示从前i个数中选,所有选法中和为j的方法。
状态转移方程:状态转移方程还是看第i个位置:
在这里插入图片描述
如果能选的话,则方法总数就是选和不选的和:dp[i][j]+=dp[i-1][j-nums[i]],但是有一个条件:j>nums[i]

代码:

class Solution 
{
public:
    int findTargetSumWays(vector<int>& nums, int target)
    {
        int n = nums.size();
        int sum = 0;
        for (auto e : nums)sum += e;
        int aim = (sum + target) / 2;
        if (aim < 0 || (sum + target) % 2 == 1)return 0;
        vector<vector<int>> dp(n + 1, vector<int>(aim + 1));
        dp[0][0] = 1;
        for (int i = 1;i <= n;i++)
        {
            for (int j = 0;j <= aim;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[n][aim];
    }
};

运行结果:
在这里插入图片描述

🍉3.最后一块石头的重量Ⅱ

题目:

在这里插入图片描述
这道题的题目是“最后一块石头的重量 II”。

样例输出和输入:

在这里插入图片描述

给定一堆石头的重量数组stones,在每一回合中,选出两块石头粉碎,最后剩下的石头的重量可能为:

  1. 如果选出的两块石头重量相等,那么两块石头都会被完全粉碎;
  2. 如果选出的两块石头重量不相等,重量为较小的石头将会完全粉碎,而重量为较大的石头新重量为较大石头的重量减去较小石头的重量。

问题要求找到最后剩下的石头的最小可能重量。如果没有剩下的石头,返回0。给定一堆石头的重量数组stones,在每一回合中,选出两块石头粉碎,最后剩下的石头的重量可能为:

  1. 如果选出的两块石头重量相等,那么两块石头都会被完全粉碎;
  2. 如果选出的两块石头重量不相等,重量为较小的石头将会完全粉碎,而重量为较大的石头新重量为较大石头的重量减去较小石头的重量。

问题要求找到最后剩下的石头的最小可能重量。如果没有剩下的石头,返回0。

算法原理:

首先我们模拟一遍这个过程:
在这里插入图片描述

注意:上述过程是建立在前一个比后一个大的基础上模拟的
我们可以发现:当中的数也是有正有负,相当与也可以分成两个分类,一个正一个负:
在这里插入图片描述
在这里插入图片描述
其实我们不难看出,当a和b最接近的时候,这时候差值最小,所以这道题我们就可以转换成了0/1背包问题:
相当于a和b都接近于sum/2,所以这道题的背包容量是sum/2,
状态表示:dp[i][j]表示前i个物品中选,总和不超过j的,最大的和。
状态转移方程:在这里插入图片描述
当存在第一种情况的是否需要和不选的情况求一个最大值:dp[i][j]=max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]
代码:

int lastStoneWeightII(vector<int>& stones)
{
	int n = stones.size();
	int sum = 0;
	for (auto e : stones)sum += e;
	vector<vector<int>> dp(n + 1, vector<int>(sum / 2 + 1));
	for (int i = 1;i <= n;i++)
	{
		for (int j = 1;j <= sum / 2;j++)
		{
			dp[i][j] = dp[i - 1][j];
			if (j >= stones[i - 1])dp[i][j] = max(dp[i - 1][j - stones[i - 1]] + stones[i - 1], dp[i - 1][j]);
		}
	}
	int a = dp[n][sum / 2];
	int b = sum - a;
	return abs(a - b);
}

空间优化过的代码:二维---->一维

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones)
    {
        int n = stones.size();
        int sum = 0;
        for (auto e : stones)sum += e;
        vector<int> dp(sum / 2 + 1);
        for (int i = 1;i <= n;i++)
            for (int j = sum / 2;j >= stones[i - 1];j--)
                dp[j] = max(dp[j - stones[i - 1]] + stones[i - 1], dp[j]);
        int a = dp[sum / 2];
        int b = sum - a;
        return abs(a - b);
    }
};

运行结果:

在这里插入图片描述

🍊总结

0/1 背包问题是组合优化中的经典问题,通过研究和解决这一问题,可以深入理解动态规划的基本思想和应用。本文详细介绍了0/1背包问题的定义、数学模型及其动态规划解法,并通过C++代码示例展示了具体的实现步骤。

通过本次总结,希望读者能够掌握如何将理论知识应用于实际问题,理解状态转移方程的推导过程,以及如何优化算法以提升效率。背包问题不仅在学术研究中具有重要意义,还广泛应用于资源分配、项目管理等实际领域。掌握这一问题的解决方法,可以为解决更复杂的优化问题打下坚实的基础。

在今后的学习中,建议读者多多练习不同变种的背包问题,如完全背包、多重背包问题等,以进一步提升自己的算法设计和分析能力。感谢阅读,希望本文对你的学习和应用有所帮助。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《简历宝典》02 - 如果你是HR,你会优先打开哪份简历?

现在的求职环境不必多说&#xff0c;其实我们大家都还是很清楚的。所以&#xff0c;在这个环境下&#xff0c;写一份优秀的简历&#xff0c;目的与作用也不必多说。那么&#xff0c;这一小节呢&#xff0c;我们先从简历这份文档的文档名开始说起。 目录 1 你觉得HR们刷简历的时…

【SVN的使用-源代码管理工具-命令行的使用 Objective-C语言】

一、接下来,我们来说一个终端的命令行的使用, 1.我们说,你的电脑里边呢,有终端, 在Mac里边,你想新建一个txt,应该怎么写,对,打开文本编辑, 打开这个东西,写点儿东西,然后保存一下,保存的时候,你还要去选择格式, 现在,如果我们用命令行,可以更方便一些, 2.首…

企业用私户发工资算不算偷税?

一般来说&#xff0c;给员工发工资都是用企业的对公账户去发&#xff0c;但是&#xff0c;有的企业会用私户去发工资&#xff0c;早前就有蜜雪冰城股东用私户给员工发奖金被税局稽查&#xff0c;最终补缴个税近800万的新闻&#xff0c;可见&#xff0c;私户发工资是具有很大风险…

上海时尚新品发布会,可以邀请哪些媒体

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 在上海举办时尚新品发布会时&#xff0c;可以邀请的媒体类型多样&#xff0c;以下是一些建议的媒体类型及其特点&#xff1a; 一、平面媒体 报纸&#xff1a; 《文汇报》&#xff1a;上…

底层软件 | 十分详细,为了学习设备树,我写了5w字笔记!

0、设备树是什么&#xff1f;1、DTS 1.1 dts简介1.2 dts例子 2、DTC&#xff08;Device Tree Compiler&#xff09;3、DTB&#xff08;Device Tree Blob&#xff09;4、绑定&#xff08;Binding&#xff09;5、Bootloader compatible属性 7、 #address-cells和#size-cells属性8…

Qt源码解析之QObject

省去大部分virtual和public方法后&#xff0c;Qobject主要剩下以下成员&#xff1a; //qobject.h class Q_CORE_EXPORT Qobject{Q_OBJECTQ_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)Q_DECLARE_PRIVATE(QObject) public:Q_I…

印章谁在管、谁用了、用在哪?契约锁让您打开手机一看便知

“印章都交给谁在管”、“哪些人能用”、“都有哪些业务在用”…这些既是管理者最关心的印章问题也是影响印章安全的关键要素。但是公司旗下分子公司那么多&#xff0c;各类公章、法人章、财务章、合同章一大堆&#xff0c;想“问”明白很难。 契约锁电子签及印控平台推出“印章…

OpenLayers使用2

接着上一篇https://blog.csdn.net/weixin_51416826/article/details/140161160?spm1001.2014.3001.5502 本篇主要内容是基于高德API逆向地址解析获取城市中心点&#xff0c;并且设置了输入框&#xff0c;可以输入城市执行飞行&#xff0c;同时基于高德API获取城市天气信息&am…

【漏洞复现】万户协同办公平台——反序列化

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 万户协同办公平台ezEIP是一个综合信息基础应用平台&#xff0c;…

Leaflet【六】绘制交互图形、测量、经纬度展示

本文主要探讨了如何利用leaflet-draw插件在地图上绘制图形&#xff0c;以及通过leaflet-measure测量距离和面积&#xff0c;并将经纬度绘制到地图上。首先&#xff0c;我们使用leaflet-draw插件&#xff0c;该插件提供了一种简单而直观的方式来绘制各种形状&#xff08;如点、线…

配置基于不同IP地址的虚拟主机

定义配置文件vhost.conf <directory /www> allowoverride none require all granted </directory> <virtualhost 192.168.209.136:80> documentroot /www servername 192.168.209.136 </virtualhost><virtualhost 192.168.209.138:80> document…

Restore Equipment

Restore Equipment 魔兽世界 - 盗号申请 - 恢复装备流程 魔兽和网易真的不行啊 1&#xff09;这个装备本来就是兑换的竟然可以卖NPC 2&#xff09;针对这个情况竟然无法挽回 3&#xff09;设计理念真的不得不吐槽一下 4&#xff09;策划真的不咋样&#xff0c;要是有机会我要自…

STM32F1+HAL库+FreeTOTS学习5——内核中断管理及中断控制函数

STM32F1HAL库FreeTOTS学习5——中断管理和临界段代码保护 中断简介中断优先级寄存器拓展FreeRTOS中PendSV和Systick中断优先级配置三个中断屏蔽寄存器FreeRTOS中断管理函数代码验证 上一期我们学习了FreeRTOS中任务挂起与恢复&#xff0c;在中断服务程序中恢复任务过程中&#…

Fish Speech: 开源文本转语音技术(TTS)的新里程碑

简介 Fish Speech 是一个全新的文本转语音(TTS)解决方案&#xff0c;该项目由fishaudio开发。当前模型使用约十五万小时三语数据训练&#xff0c;对中文支持非常的完美。 能够熟练处理和生成中文、日语和英语的语音&#xff0c;语言处理能力接近人类水平&#xff0c;并且声音…

狂赚三个亿,百亿医用耗材上市公司重金押注老人轮椅

布局海外市场&#xff0c;轮椅销量翻两番 作者 | 艾米莉 排版 | 张思琪 抛砖引玉 1.年销售60万台轮椅&#xff0c;英科医疗如何做到&#xff1f; 2.老年人轮椅是出海&#xff0c;还是深耕国内市场&#xff1f; 3.2022年全球轮椅市场规模为48亿美元&#xff0c;谁在喝汤&…

Android仿天眼查人物关系图

效果图预览 绘制思路 这里使用了中学解析几何知识 XPoint OPointX OPointXcosθ&#xff1b; YPoint OPointY OPointYsinθ&#xff1b; canvas.drawText(lists.get(i).getName(), XPoint (float) Math.cos(pere * i 5) * radius[i % radius.length] - 30, YPoint (fl…

【笔试记录】腾讯音乐 | 20230903 | cpp (更新ing)

1 完美数 1.1 题目描述 小红定义一个数为“完美数”&#xff0c;当且仅当该数仅有一个非零数字。例如 5000, 4, 1, 10, 200 都是完美数。 小红拿到了一个大小为 n&#xff08;2 < n < 2000&#xff09;的数组 a&#xff0c;她希望选择数组中的两个元素&#xff08;1 …

CVE-2023-30212(xss漏洞)

简介 OURPHP版本<7.2.0存在XSS漏洞&#xff0c;攻击路径为/client/manage/ourphp_out.php。 过程 打开靶场 访问攻击路径/client/manage/ourphp_out.php 得到flag{354c7c41-cc23-4de5-be73-79cbbf384aba}

上海计算机考研炸了,这所学校慎报!上海大学计算机考研考情分析!

上海大学&#xff08;Shanghai University&#xff09;&#xff0c;简称“上大”&#xff0c;是上海市属、国家“211工程”重点建设的综合性大学&#xff0c;教育部与上海市人民政府共建高校&#xff0c;国防科技工业局与上海市人民政府共建高校&#xff0c;国家“双一流”世界…

leetcode--二叉搜索子树的最大键值和

leetcode地址&#xff1a;二叉搜索子树的最大键值和 给你一棵以 root 为根的 二叉树 &#xff0c;请你返回 任意 二叉搜索子树的最大键值和。 二叉搜索树的定义如下&#xff1a; 任意节点的左子树中的键值都 小于 此节点的键值。 任意节点的右子树中的键值都 大于 此节点的键值…