代码随想录第三十六天(一刷C语言)|背包问题理论基础分割等和子集

创作目的:为了方便自己后续复习重点,以及养成写博客的习惯。

一、背包问题

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

思路:参考carl文档。

1、确定dp数组以及下标的含义:

(1)使用二维数组,即dp[i][j] 表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。

(2)使用一维数组,dp[j]表示容量为j的背包,所背的物品价值可以最大为dp[j]。

2、确定递推公式:

二维数组:

(1)不放物品i:背包容量为j,里面不放物品i的最大价值,推出dp[i - 1][j]。

(2)放物品i:dp[i - 1][j - weight[i]] 为背包容量为j - weight[i]的时候不放物品i的最大价值,故背包放物品i得到的最大价值是dp[i - 1][j - weight[i]] + value[i] (物品i的价值)。

故递归公式为: dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i])。

一维数组:

        dp[j - weight[i]]表示容量为j - weight[i]的背包所背的最大价值。dp[j - weight[i]] + value[i] 表示 容量为 j - 物品i重量 的背包 加上 物品i的价值。故而递推公式为:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);

3、dp数组如何初始化:

二维数组:

        由递推公式知i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。

当 j < weight[0]时,既背包容量比编号0的物品重量还小,dp[0][j] 为是0。

当j >= weight[0]时,既背包容量放足够放编号0物品,dp[0][j] 应该是value[0]。

由递推公式知dp[i][j] 是由左上方数值推导出来了,那么 其他下标初始为什么数值都可以,因为都会被覆盖。

一维数组:假设物品价值都是大于0的,dp数组初始化的时候,都初始为0。

4、确定遍历的维度:

二维数组:

有两个遍历的维度,物品与背包重量。先遍历背包或先遍历背包重量均可。

一维数组:一维dp遍历的时候,背包是从大到小。采用倒序遍历。

5、分别举例推导dp数组

二维数组AC代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define ARR_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
#define BAG_WEIGHT 4void backPack(int* weights, int weightSize, int* costs, int costSize, int bagWeight) {// 开辟dp数组int dp[weightSize][bagWeight + 1];memset(dp, 0, sizeof(int) * weightSize * (bagWeight + 1));int i, j;// 当背包容量大于物品0的重量时,将物品0放入到背包中for(j = weights[0]; j <= bagWeight; ++j) {dp[0][j] = costs[0];}// 先遍历物品,再遍历重量for(j = 1; j <= bagWeight; ++j) {for(i = 1; i < weightSize; ++i) {// 如果当前背包容量小于物品重量if(j < weights[i])// 背包物品的价值等于背包不放置当前物品时的价值dp[i][j] = dp[i-1][j];// 若背包当前重量可以放置物品else// 背包的价值等于放置该物品或不放置该物品的最大值dp[i][j] = MAX(dp[i - 1][j],  dp[i - 1][j - weights[i]] + costs[i]);}}printf("%d\n", dp[weightSize - 1][bagWeight]);
}int main(int argc, char* argv[]) {int weights[] = {1, 3, 4};int costs[] = {15, 20, 30};backPack(weights, ARR_SIZE(weights), costs, ARR_SIZE(costs), BAG_WEIGHT);return 0;
}

一维数组AC代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#define ARR_SIZE(a) (sizeof((a)) / sizeof((a)[0]))
#define BAG_WEIGHT 4void backPack(int* weights, int weightSize, int* costs, int costSize, int bagWeight) {// 开辟dp数组int dp[weightSize][bagWeight + 1];memset(dp, 0, sizeof(int) * weightSize * (bagWeight + 1));int i, j;// 当背包容量大于物品0的重量时,将物品0放入到背包中for(j = weights[0]; j <= bagWeight; ++j) {dp[0][j] = costs[0];}// 先遍历物品,再遍历重量for(j = 1; j <= bagWeight; ++j) {for(i = 1; i < weightSize; ++i) {// 如果当前背包容量小于物品重量if(j < weights[i])// 背包物品的价值等于背包不放置当前物品时的价值dp[i][j] = dp[i-1][j];// 若背包当前重量可以放置物品else// 背包的价值等于放置该物品或不放置该物品的最大值dp[i][j] = MAX(dp[i - 1][j],  dp[i - 1][j - weights[i]] + costs[i]);}}printf("%d\n", dp[weightSize - 1][bagWeight]);
}int main(int argc, char* argv[]) {int weights[] = {1, 3, 4};int costs[] = {15, 20, 30};backPack(weights, ARR_SIZE(weights), costs, ARR_SIZE(costs), BAG_WEIGHT);return 0;
}

二、分割等和子集

思路:参考carl文档。

本题要求集合里能否出现总和为 sum / 2 的子集。要理解题目中物品是nums[i],重量是nums[i],价值也是nums[i],背包体积是sum/2。

(1)0/1背包问题

        明确背包的体积为sum / 2。背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值。背包如果正好装满,说明找到了总和为 sum / 2 的子集。背包中每一个元素是不可重复放入。

(2)动规五部曲

1、确定dp数组以及下标的含义: dp[j]表示 背包总容量(所能装的总重量)是j,放进物品后,背的最大重量为dp[j]。若背包容量为target, dp[target]就是装满 背包之后的重量,所以 当 dp[target] == target 的时候,背包就装满了。

2、确定递推公式:背包的递推公式为dp[j] = max(dp[j], dp[j - weight[i]] + value[i])。题意相当于背包里放入数值,物品i的重量是nums[i],其价值也是nums[i]。故而递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i]);

3、dp数组的初始化:从dp[j]的定义来看,dp[0]等于0。题目给的价值都是正整数那么非0下标都初始化为0,若题目给的价值有负数,那么非0下标就要初始化为负无穷。

4、确定遍历的顺序:如果使用一维dp数组,物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历。

5、举例dp数组:明确如果dp[j] == j 说明,集合中的子集总和正好可以凑成总和j。

lecode题目:https://leetcode.cn/problems/partition-equal-subset-sum/description/

二维数组AC代码:

/**
1. dp数组含义:dp[i][j]为背包重量为j时,从[0-i]元素和最大值
2. 递推公式:dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i])
3. 初始化:dp[i][0]初始化为0。因为背包重量为0时,不可能放入元素。dp[0][j] = nums[0],当j >= nums[0] && j < target时
4. 遍历顺序:先遍历物品,再遍历背包
*/
#define MAX(a, b) (((a) > (b)) ? (a) : (b))int getSum(int* nums, int numsSize) {int sum = 0;int i;for(i = 0; i < numsSize; ++i) {sum += nums[i];}return sum;
}bool canPartition(int* nums, int numsSize){// 求出元素总和int sum = getSum(nums, numsSize);// 若元素总和为奇数,则不可能得到两个和相等的子数组if(sum % 2)return false;// 若子数组的和等于target,则nums可以被分割int target = sum / 2;// 初始化dp数组int dp[numsSize][target + 1];// dp[j][0]都应被设置为0。因为当背包重量为0时,不可放入元素memset(dp, 0, sizeof(int) * numsSize * (target + 1));int i, j;// 当背包重量j大于nums[0]时,可以在dp[0][j]中放入元素nums[0]for(j = nums[0]; j <= target; ++j) {dp[0][j] = nums[0];}for(i = 1; i < numsSize; ++i) {for(j = 1; j <= target; ++j) {// 若当前背包重量j小于nums[i],则其值等于只考虑0到i-1物品时的值if(j < nums[i])dp[i][j] = dp[i - 1][j];// 否则,背包重量等于在背包中放入num[i]/不放入nums[i]的较大值elsedp[i][j] = MAX(dp[i - 1][j], dp[i - 1][j  - nums[i]] + nums[i]);}}// 判断背包重量为target,且考虑到所有物品时,放入的元素和是否等于targetreturn dp[numsSize - 1][target] == target;
}

一维数组AC代码:

/**
1. dp数组含义:dp[j]为背包重量为j时,其中可放入元素的最大值
2. 递推公式:dp[j] = max(dp[j], dp[j - nums[i]] + nums[i])
3. 初始化:均初始化为0即可
4. 遍历顺序:先遍历物品,再后序遍历背包
*/
#define MAX(a, b) (((a) > (b)) ? (a) : (b))int getSum(int* nums, int numsSize) {int sum = 0;int i;for(i = 0; i < numsSize; ++i) {sum += nums[i];}return sum;
}bool canPartition(int* nums, int numsSize){// 求出元素总和int sum = getSum(nums, numsSize);// 若元素总和为奇数,则不可能得到两个和相等的子数组if(sum % 2)return false;// 背包容量int target = sum / 2;// 初始化dp数组,元素均为0int dp[target + 1];memset(dp, 0, sizeof(int) * (target + 1));int i, j;// 先遍历物品,后遍历背包for(i = 0; i < numsSize; ++i) {for(j = target; j >= nums[i]; --j) {dp[j] = MAX(dp[j], dp[j - nums[i]] + nums[i]);}}// 查看背包容量为target时,元素总和是否等于targetreturn dp[target] == target;
}

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

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

相关文章

Docker的安装及使用

目录 安装Docker 安装yum工具 更新本地镜像源 安装docker 启动docker 关闭防火墙 docker启动命令 配置镜像加速 docker的使用 拉取nginx 查看本地镜像 把镜像文件nginx导出成tar文件 查看是否导出成功 ​编辑 删除本地镜像nginx:latest 导入镜像文件nginx 拉取…

Java项目-瑞吉外卖项目优化Day1

创建新仓库 push项目 新建分支v1.0做优化 导入Redis相关配置 导入坐标。 实现配置类&#xff0c;重写序列化器&#xff0c;也可以直接用StringRedisTemplate。 application.xml配置&#xff1a; 实现缓存短信验证码 将手机号与验证码存进redis。 从redis中获取验证码&…

微信小程序长按图片识别二维码

设置show-menu-by-longpress"true"即可&#xff0c;长按图片后会弹出一个菜单&#xff0c;若图片中包含二维码或小程序码&#xff0c;菜单中会有响应入口 <image src"图片地址" show-menu-by-longpress"true"></image>官方说明

大语言模型(LLM)与 Jupyter 连接起来了!

现在&#xff0c;大语言模型&#xff08;LLM&#xff09;与 Jupyter 连接起来了&#xff01; 这主要归功于一个名叫 Jupyter AI 的项目&#xff0c;它是官方支持的 Project Jupyter 子项目。目前该项目已经完全开源&#xff0c;其连接的模型主要来自 AI21、Anthropic、AWS、Co…

专栏十六:bulk以及单细胞空转中的progeny通路分析

progeny本身有自己的R包,可以提取通路基因集信息,团队把他嵌入另一个R包decoupleR中完成富集分析。decoupleR自己有详细的针对bulk和scRNAseq的教程 简单安装一下 devtools::install_github(saezlab/OmnipathR) devtools::install_github("saezlab/progeny") Bio…

6 最大积水量

蛮力求解 #include <iostream> using namespace::std; using std::cout; using std::cin; int zdjsl(int n, int height[]) {int sum 0;int left_max[n];int right_max[n];left_max[0] height[0];right_max[n-1] height[n-1];for(int i1; i<n; i){left_max[i] m…

arcmap + oracle11g 迁移数据 报错 copyFeatures失败

原因排查&#xff1a; 1.通过这个界面&#xff0c;我们无法查到真正的原因&#xff0c; 2.将数据拷贝到我们自己的arcmap服务器中&#xff0c;采用 单个要素 导入&#xff0c;从result面板中查找原因&#xff1b; 从上面这个图中&#xff0c;看到关键信息 DBMS error ORA-016…

C++ STL——栈和队列(stack queue)

本节目标 1.stack的介绍和使用及其模拟实现 2.queue的介绍和使用及其模拟实现 3.priority_queue的介绍和使用及其模拟实现 4.容器适配器 1.stack的介绍和使用及其模拟实现 1.1 stack的介绍 stack的文档介绍 根据stack的文档介绍可以知道&#xff0c;stack是一种容器适配器…

关于“Python”的核心知识点整理大全29

目录 11.2.4 方法 setUp() 注意 11.3 小结 第二部分 项目1 外星人入侵 第&#xff11;2 章 武装飞船 注意 12.1 规划项目 12.2 安装 Pygame 注意 12.2.1 使用 pip 安装 Python 包 注意 如果你启动终端会话时使用的是命令python3&#xff0c;那么在这里应使用命令…

【python VS vba】(10) 在python使用matplotlib库来画不同的图形

7 下面是不同类型的图形 6 比如 散点图 sactter import numpy as np import matplotlib.pyplot as plt# 散点图 # x, y x np.random.normal(0, 1, 20) y np.random.normal(0, 1, 20)# 绘制散点图 plt.scatter(x, y, s25, alpha0.75)plt.xlabel("X") plt.ylabel(&…

Linux多版本cuda切换

目标 将cuda版本从10.0切换为11.1 步骤 查看当前cuda版本&#xff1a; nvcc -V编辑.bashrc文件&#xff1a; vim ~/.bashrc在文件中添加以下几行&#xff08;若已存在则忽略&#xff09;&#xff1a; export PATH$PATH:/usr/local/cuda/bin export LD_LIBRARY_PATH$LD_LI…

全网好听的BGM都在这里下载,赶紧收藏好了

无论是自媒体创作者还是从事视频剪辑工作的朋友&#xff0c;对于BGM的选择都很重要&#xff0c;一首适配的BGM能大大提升你作品的质量&#xff0c;还能让作品更优秀。哪里才能找到好听又免费的BGM&#xff1f;下面推荐几个我多年收藏的6个音效、音频素材网站&#xff0c;赶紧收…

python 1200例——【1】九九乘法表

在Python中&#xff0c;你可以使用两个嵌套的for循环来打印九九乘法表。以下是一个简单的例子&#xff1a; for i in range(1, 10):for j in range(1, i1):print(f"{j}x{i}{j*i}", end"\t")print()这段代码的工作原理如下&#xff1a; 外层循环 for i in…

【leetcode203】移除链表元素【Java代码讲解】

12.18 移除链表元素 给你一个链表的头节点 head 和一个整数 val &#xff0c;请你删除链表中所有满足 Node.val val 的节点&#xff0c;并返回 新的头节点 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,6,3,4,5,6], val 6 输出&#xff1a;[1,2,3,4,5]示例 2&#xff…

定制 Electron 窗口标题栏

Electron 是一款流行的桌面应用开发框架&#xff0c;基于 Web 技术构建&#xff0c;提供了强大的跨平台能力。在开发过程中&#xff0c;经常需要定制窗口标题栏以创造独特的用户体验。 1. 完全隐藏默认标题栏 有时候&#xff0c;我们希望创建一个自定义的标题栏&#xff0c;完…

JVM调优排错专题

JVM调优排错专题 1 打开MAT报错 1 打开MAT报错 下载了linux版本的 MAT 软件&#xff0c;1.15.0版本。 下载地址&#xff1a;https://eclipse.dev/mat/downloads.php 运行时报错了。 错误截图 报错日志 wittasus:/usr/develop/mat$ ./MemoryAnalyzer Unrecognized option:…

基于Java SSM框架实现宠物医院信息管理系统项目【项目源码】

基于java的SSM框架实现宠物医院信息管理系统演示 java简介 Java语言是在二十世纪末由Sun公司发布的&#xff0c;而且公开源代码&#xff0c;这一优点吸引了许多世界各地优秀的编程爱好者&#xff0c;也使得他们开发出当时一款又一款经典好玩的小游戏。Java语言是纯面向对象语言…

Kafka 安装与部署

目录 Kafka 下载 &#xff08;1&#xff09;将 kafka_2.11-2.4.1.tgz 上传至 /opt/software/ &#xff08;2&#xff09;解压安装包至 /opt/module/ [huweihadoop101 ~]$ cd /opt/software/ [huweihadoop101 software]$ tar -zxvf kafka_2.11-2.4.1.tgz -C ../module/&#…

基础算法(5):滑动窗口

1.何为滑动窗口&#xff1f; 滑动窗口其实也是一种算法&#xff0c;主要有两类&#xff1a;一类是固定窗口&#xff0c;一类是可变窗口。固定的窗口只需要一个变量记录&#xff0c;而可变窗口需要两个变量。 2.固定窗口 就像上面这个图一样。两个相邻的长度为4的红色窗口&…

JavaSE 泛型

目录 1 泛型类的定义1.1 为什么需要泛型1.2 泛型的概念1.3 泛型的分类 2 泛型类2.1 泛型类的定义2.2 泛型类的例子2.3 泛型类的实例化2.3.1 实例化语法2.3.2 裸类型(Raw Type) 2.4 泛型类的定义-类型边界2.5 泛型类的使用-通配符(Wildcards)2.5.1 基本概念2.5.2 通配符-上界2.5…