【动态规划】背包问题 {01背包问题;完全背包问题;二维费用背包问题}

一、背包问题概述

背包问题(Knapsackproblem)是⼀种组合优化的NP完全问题。

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

根据物品的个数,分为如下几类:

  • 01背包问题:每个物品只有⼀个
  • 完全背包问题:每个物品有⽆限多个
  • 多重背包问题:每件物品最多有si个
  • 混合背包问题:每个物品会有上⾯三种情况…
  • 分组背包问题:物品有n组,每组物品里有若干个,每组里最多选一个物品

其中上述分类里面,根据背包是否装满,⼜分为两类:

  • 不⼀定装满背包
  • 背包⼀定装满

优化⽅案:

  • 空间优化-滚动数组
  • 单调队列优化
  • 贪⼼优化

根据限定条件的个数,又分为两类:

  • 限定条件只有⼀个:比如体积->普通的背包问题
  • 限定条件有两个:比如体积+重量->⼆维费⽤背包问题

根据不同的问法,又分为很多类:

  • 输出⽅案
  • 求⽅案总数
  • 最优⽅案
  • ⽅案可⾏性

其实还有很多分类,但是我们仅需了解即可。
因此,背包问题种类非常繁多,题型非常丰富,难度也是非常难以捉摸。但是,尽管种类非常多,都是从01背包问题演化过来的。所以,⼀定要把01背包问题学好。


二、相关编程题

2.1 01背包问题

2.1.1 01背包(模板)

题目链接

【模板】01背包_牛客题霸_牛客网 (nowcoder.com)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

#include <iostream>
#include <cstring>
using namespace std;
#define N 1010int n, V;
int v[N], w[N];
int dp[N][N];int main() {//读入数据cin >> n >> V;for(int i = 1; i <= n; ++i) //注意所有下标从1开始{cin >> v[i] >> w[i];}//解决第一问for(int i = 1; i <= n; ++i){for(int j = 1; j <= V; ++j){dp[i][j] = dp[i-1][j]; // 不选i物品if(j>=v[i]) //要保证不越界,有空间存放v[i]dp[i][j] = max(dp[i][j], w[i]+dp[i-1][j-v[i]]); //选i物品}}cout << dp[n][V] << endl;//解决第二问memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; ++j) dp[0][j] = -1; //-1表示恰好装满体积j无解for(int i = 1; i <= n; ++i){for(int j = 1; j <= V; ++j){dp[i][j] = dp[i-1][j]; // 不选i物品if(j>=v[i] && dp[i-1][j-v[i]]!=-1) //条件1保证不越界,有空间存放v[i];条件2保证恰好装满dp[i][j] = max(dp[i][j], w[i]+dp[i-1][j-v[i]]); //选i物品}}cout << (dp[n][V]==-1? 0:dp[n][V]) << endl;
}//空间优化
int dp[N];int main() {//读入数据...//解决第一问for(int i = 1; i <= n; ++i){for(int j = V; j>=v[i]; --j) //注意从右往左遍历dp[j] = max(dp[j], w[i]+dp[j-v[i]]);}cout << dp[V] << endl;//解决第二问memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; ++j) dp[j] = -1; //-1表示恰好装满体积j无解for(int i = 1; i <= n; ++i){for(int j = V; j>=v[i]; --j){if(dp[j-v[i]]!=-1)dp[j] = max(dp[j], w[i]+dp[j-v[i]]);}}cout << (dp[V]==-1? 0:dp[V]) << endl;
}

2.1.2 分割等和子集

题目链接

416. 分割等和子集 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

  • 01背包必须装满
  • 为什么不用-1标识无解?因为其状态表示中自带无解判断(false)

编写代码

//空间优化
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;vector<bool> dp(sum/2+1);dp[0] = true;for(int i = 1; i <= n; ++i){for(int j = sum/2; j >= nums[i-1]; --j)dp[j] = dp[j] || dp[j-nums[i-1]];}return dp[sum/2];}
};

2.1.3 目标和

题目链接

494. 目标和 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

  • 01背包必须装满
  • 为什么不用-1标识无解?因为其状态表示中自带无解判断(0种选法)

编写代码

//空间优化
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int n = nums.size();int sum = 0;for(auto e : nums) sum+=e;int a = (sum+target)/2;if(a<0 || (sum+target)%2==1) return false; //a是所有正数的和;必须是偶数能被2整除;vector<int> dp(a+1);dp[0] = 1;for(int i = 1; i <= n; ++i){for(int j = a; j >= nums[i-1]; --j)dp[j] += dp[j-nums[i-1]];}return dp[a];}
};

2.1.4 最后一块石头的重量Ⅱ

题目链接

1049. 最后一块石头的重量 II - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

  • 01背包不必装满

编写代码

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

2.2 完全背包问题

2.2.1 完全背包(模板)

题目链接

【模板】完全背包_牛客题霸_牛客网 (nowcoder.com)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

和01背包唯一的不同点就是每个物品可以选择无数次,因此在选择i物品的状态转移方程中有所变化。

提示:综合了01背包、通配符匹配、正则表达式匹配的考点

编写代码

#include <iostream>
#include <cstring>
using namespace std;const int N = 1010;
int n, V;
int v[N], w[N];
int dp[N][N];int main() {//读入数据cin >> n >> V;for(int i = 1; i <= n; ++i)cin >> v[i] >> w[i];//解决第一问for(int i = 1; i <= n; ++i){for(int j = 1; j <= V; ++j){dp[i][j] = dp[i-1][j];if(j >= v[i])dp[i][j] = max(dp[i][j], dp[i][j-v[i]]+w[i]);}}cout << dp[n][V] << endl;//解决第二问memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; ++j) dp[0][j] = -1;for(int i = 1; i <= n; ++i){for(int j = 1; j <= V; ++j){dp[i][j] = dp[i-1][j];if(j >= v[i] && dp[i][j-v[i]]!=-1)dp[i][j] = max(dp[i][j], dp[i][j-v[i]]+w[i]);}}cout << (dp[n][V]==-1? 0:dp[n][V]) << endl;
}//空间优化
int dp[N];int main() {//读入数据...//解决第一问for(int i = 1; i <= n; ++i){for(int j = v[i]; j <= V; ++j) //注意从左往右遍历{dp[j] = max(dp[j], dp[j-v[i]]+w[i]);}}cout << dp[V] << endl;//解决第二问memset(dp, 0, sizeof(dp));for(int j = 1; j <= V; ++j) dp[j] = -1;for(int i = 1; i <= n; ++i){for(int j = v[i]; j <= V; ++j){if(dp[j-v[i]]!=-1)dp[j] = max(dp[j], dp[j-v[i]]+w[i]);}}cout << (dp[V]==-1? 0:dp[V]) << endl;
}

2.2.2 零钱兑换

题目链接

322. 零钱兑换 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

提示:不能再像完全背包那样用-1表示无解。因为这里求的是最小值,如果使用-1,而不选i位置无解,即使选择i位置的零钱有解,也会在取min时取到-1。实际上我们设置无解为-1的初衷就是为了不参与后续的比较,因此可以用INF(无穷大)表示无解,这样的话,在取min时永远不会取到INF。

编写代码

class Solution {
public:int coinChange(vector<int>& coins, int amount) {const int INF = 0x3f3f3f3f;int n = coins.size();vector<vector<int>> dp(n+1, vector<int>(amount+1));for(int j = 1; j <= amount; ++j) dp[0][j] = INF;for(int i = 1; i <= n; ++i){for(int j = 1; j <= amount; ++j){dp[i][j] = dp[i-1][j];if(j >= coins[i-1])dp[i][j] = min(dp[i][j], dp[i][j-coins[i-1]]+1);}}return dp[n][amount]>=INF? -1:dp[n][amount];}
};//空间优化
class Solution {
public:int coinChange(vector<int>& coins, int amount) {const int INF = 0x3f3f3f3f;int n = coins.size();vector<int> dp(amount+1);for(int j = 1; j <= amount; ++j) dp[j] = INF;for(int i = 1; i <= n; ++i){for(int j = coins[i-1]; j <= amount; ++j)dp[j] = min(dp[j], dp[j-coins[i-1]]+1);}return dp[amount]>=INF? -1:dp[amount];}
};

2.2.3 零钱兑换Ⅱ

题目链接

518. 零钱兑换 II - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:int change(int amount, vector<int>& coins) {int n = coins.size();vector<int> dp(amount+1);dp[0] = 1;for(auto e : coins){for(int j = e; j <= amount; ++j)dp[j]+=dp[j-e];  }return dp[amount];}
};

2.2.4 完全平方数

题目链接

279. 完全平方数 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

编写代码

class Solution {
public:int numSquares(int n) {int m = sqrt(n);const int INF = 0x3f3f3f3f;vector<int> dp(n+1, INF);dp[0] = 0;for(int i = 1; i <= m; ++i){for(int j = i*i; j <= n; ++j)dp[j] = min(dp[j], dp[j-i*i]+1);}return dp[n];}
};

2.3 二维费用背包问题

2.3.1 一和零

题目链接

474. 一和零 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

  • 01背包,不必装满,二维费用

编写代码

//三维dp表
class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {int cnt = strs.size();vector<vector<vector<int>>> dp(cnt+1, vector<vector<int>>(m+1, vector<int>(n+1)));for(int i = 1; i <= cnt; ++i){//统计以下字符串中0,1的个数int c0 = Count0(strs[i-1]);int c1 = strs[i-1].size()-c0;for(int j = 0; j <= m; ++j)for(int k = 0; k <= n; ++k){dp[i][j][k] = dp[i-1][j][k]; //不选i位置的字符串if(j>=c0 && k>=c1) //选i位置的字符串dp[i][j][k] = max(dp[i][j][k], dp[i-1][j-c0][k-c1]+1);}}return dp[cnt][m][n];}int Count0(const string& str){int cnt = 0;for(auto ch : str)if(ch == '0') ++cnt;return cnt;}
};//空间优化:二维dp表
class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {int cnt = strs.size();vector<vector<int>> dp(m+1, vector<int>(n+1));for(int i = 1; i <= cnt; ++i){int c0 = Count0(strs[i-1]);int c1 = strs[i-1].size()-c0;for(int j = m; j >= c0; --j) //注意从大到小遍历for(int k = n; k >= c1; --k){dp[j][k] = max(dp[j][k], dp[j-c0][k-c1]+1);}}return dp[m][n];}
};

2.3.2 盈利计划

题目链接

879. 盈利计划 - 力扣(LeetCode)

题目描述

在这里插入图片描述

算法原理

在这里插入图片描述

提示:k可以小于p[i],表示单单i项计划的盈利就超过了最低限度k。但是作为数组的下标k-p[i]不能是负数,因此我们采取一个折中的方案,当k-p[i]<0时从前i-1项计划中选择的总利润最低为0即可。

编写代码

//三维dp表
class Solution {
public:int profitableSchemes(int n, int m, vector<int>& group, vector<int>& profit) {int cnt = group.size();vector<vector<vector<int>>> dp(cnt+1, vector<vector<int>>(n+1, vector<int>(m+1)));for(int j = 0; j <= n; ++j) dp[0][j][0] = 1; //没有计划,没有利润,无论多少人数都可以选空for(int i = 1; i <= cnt; ++i){for(int j = 0; j <= n; ++j){for(int k = 0; k <= m; ++k){dp[i][j][k] = dp[i-1][j][k];if(j>=group[i-1])dp[i][j][k] += dp[i-1][j-group[i-1]][max(0, k-profit[i-1])];dp[i][j][k] %= (int)1e9+7;}}}return dp[cnt][n][m];}
};//空间优化:二维dp表
class Solution {
public:int profitableSchemes(int n, int m, vector<int>& group, vector<int>& profit) {int cnt = group.size();vector<vector<int>> dp(n+1, vector<int>(m+1));for(int j = 0; j <= n; ++j) dp[j][0] = 1;for(int i = 1; i <= cnt; ++i){for(int j = n; j >= group[i-1]; --j)for(int k = m; k >= 0; --k){dp[j][k] += dp[j-group[i-1]][max(0, k-profit[i-1])];dp[j][k] %= (int)1e9+7;}}return dp[n][m];}
};

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

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

相关文章

链接追踪系列-07.logstash安装json_lines插件

进入docker中的logstash 容器内&#xff1a; jelexbogon ~ % docker exec -it 7ee8960c99a31e607f346b2802419b8b819cc860863bc283cb7483bc03ba1420 /bin/sh $ pwd /usr/share/logstash $ ls bin CONTRIBUTORS Gemfile jdk logstash-core modules tools x-pack …

语音识别概述

语音识别概述 一.什么是语音&#xff1f; 语音是语言的声学表现形式&#xff0c;是人类自然的交流工具。 图片来源&#xff1a;https://www.shenlanxueyuan.com/course/381 二.语音识别的定义 语音识别&#xff08;Automatic Speech Recognition, ASR 或 Speech to Text, ST…

基于RAG大模型的变电站智慧运维-第十届Nvidia Sky Hackathon参赛作品

第十届Nvidia Sky Hackathon参赛作品 1. 项目说明 变电站是用于变电的设施&#xff0c;主要的作用是将电压转化&#xff0c;使电能在输电线路中能够长距离传输。在电力系统中&#xff0c;变电站起到了极为重要的作用&#xff0c;它可以完成电能的负荷分配、电压的稳定、容错保…

电影购票小程序论文(设计)开题报告

一、课题的背景和意义 随着互联网技术的不断发展&#xff0c;人们对于购票的需求也越来越高。传统的购票方式存在着排队时间长、购票流程繁琐等问题&#xff0c;而网上购票则能够有效地解决这些问题。电影购票小程序是网上购票的一种新型应用&#xff0c;它能够让用户随时随地…

06.截断文本 选择任何链接 :root 和 html 有什么区别

截断文本 对超过一行的文本进行截断,在末尾添加省略号(…)。 使用 overflow: hidden 防止文本超出其尺寸。使用 white-space: nowrap 防止文本超过一行高度。使用 text-overflow: ellipsis 使得如果文本超出其尺寸,将以省略号结尾。为元素指定固定的 width,以确定何时显示省略号…

Selenium WebDriver中的显式等待与隐式等待:深入理解与应用

在自动化测试中&#xff0c;尤其是在使用Selenium WebDriver进行Web应用的自动化测试时&#xff0c;等待元素加载完成是一个常见的需求。Selenium提供了两种等待机制来处理这一问题&#xff1a;显式等待&#xff08;Explicit Wait&#xff09;和隐式等待&#xff08;Implicit W…

笔记 4 :linux 0.11 中继续分析 0 号进程创建一号进程的 fork () 函数

&#xff08;27&#xff09;本条目开始&#xff0c; 开始分析 copy_process () 函数&#xff0c;其又会调用别的函数&#xff0c;故先分析别的函数。 get_free_page &#xff08;&#xff09; &#xff1b; 先 介绍汇编指令 scasb &#xff1a; 以及 指令 sstosd &#xff1a;…

什么是架构设计师?定义、职责和任务,全方位解析需要具备的专业素质

目录 1. 架构设计师的定义 2. 架构设计师的职责和任务 2.1 系统架构设计 2.1.1 模块划分 2.1.2 接口设计 2.1.3 通信方式 2.2 技术选型与决策 2.2.1 技术评估 2.2.2 技术选型 2.2.3 技术决策 2.3 性能优化与调优 2.3.1 性能分析 2.3.2 性能优化 2.3.3 性能调优 …

基于BitMap的工作日间隔计算

背景问题 在我们实际开发过程中&#xff0c;时常会遇到日期的间隔计算&#xff0c;即计算多少工作日之后的日期&#xff0c;在不考虑法定节假日的情况下也不是那么复杂&#xff0c;毕竟周六、周日是相对固定的&#xff0c;Java语言也提供了丰富的类来处理此问题。 然而&#x…

MVVM和MVC的原理以及它们的区别

MVVM&#xff08;Model-View-ViewModel&#xff09;和 MVC&#xff08;Model-View-Controller&#xff09;是两种常见的前端架构模式&#xff0c;它们都旨在帮助组织和管理复杂的前端应用程序逻辑和视图层。 MVC&#xff08;Model-View-Controller&#xff09; 原理&#xff1…

视图库对接系列(GA-T 1400)十七、视图库对接系列(本级)采集设备获取

背景 这一章的话,我们写写如何获取采集设备获取,之前其实也有说过类似的 就我们订阅的时候如果subscribeDetail=3的话,下级就会主动给我们推送采集设备。但这里的话,是下级主动推,如果下级平台不支持,或者说可能因为某个原因推的不全,怎么办? 我们能否主动获取采集设备…

WPF学习(4) -- 数据模板

一、DataTemplate 在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;DataTemplate 用于定义数据的可视化呈现方式。它允许你自定义如何展示数据对象&#xff0c;从而实现更灵活和丰富的用户界面。DataTemplate 通常用于控件&#xff08;如ListBox、…

知识图谱和 LLM:利用 Neo4j 实现大型语言模型

这是关于 Neo4j 的 NaLLM 项目的一篇博客文章。这个项目是为了探索、开发和展示这些 LLM 与 Neo4j 结合的实际用途。 2023 年,ChatGPT 等大型语言模型 (LLM) 因其理解和生成类似人类的文本的能力而风靡全球。它们能够适应不同的对话环境、回答各种主题的问题,甚至模拟创意写…

NSSCTF中24网安培训day1中web的题目

我flag呢 直接查看源代码即可CtrlU [SWPUCTF 2021 新生赛]Do_you_know_http 用Burpsuite抓包&#xff0c;之后在User-agent下面添加XFF头&#xff0c;即X-Forwarded-For:127.0.0.1 [SWPUCTF 2022 新生赛]funny_php 首先是php的弱比较&#xff0c;对于num参数&#xff0c;我们…

hot100 | 十一、二分搜索

1-leetcode35. 搜索插入位置 注意&#xff1a; 看Labuladong的书&#xff0c;知道while的判断符号跟left right的关系 public int searchInsert(int[] nums, int target) {int left 0;int right nums.length - 1;while (left < right) {int mid left (right - left) /…

AI如何引领个人潜力的深度挖掘

AI如何引领个人潜力的深度挖掘 人工智能&#xff08;AI&#xff09;不仅是一场技术革命&#xff0c;更是对人类自身能力的一次深刻反思。本文旨在探讨在AI时代下&#xff0c;个人如何挖掘并发挥自己的最大潜能&#xff0c;不仅在职场、教育领域找到新的定位&#xff0c;同时也…

PostgreSQL日志文件配置,记录所有操作记录

为了更详细的记录PostgreSQL 的运行日志&#xff0c;我们一般需要修改PostgreSQL 默认的配置文件&#xff0c;这里整理了一些常用的配置 修改配置文件 打开 PostgreSQL 配置文件 postgresql.conf。该文件通常位于 PostgreSQL 安装目录下的 data 文件夹中。 找到并修改以下配…

Python循环遍历:深入理解与实战应用

在Python编程中&#xff0c;循环遍历是一种基本且强大的控制流结构&#xff0c;它允许我们重复执行一段代码直到满足某个条件为止。无论是处理数据集合&#xff08;如列表、元组、字典、集合等&#xff09;&#xff0c;还是执行重复的任务&#xff0c;循环遍历都是不可或缺的工…

807.保持城市天际线

解题思路 首先找到四个主要方向&#xff08;东南西北&#xff09;的天际线情况。南北看是一样的&#xff0c;东西看也是一样的。所以统计出每行的最值&#xff0c;每列的最值&#xff0c;用一个n的数组存储。分别存储行和列的最值。最值的位置进行标记&#xff0c;然后对于其余…

【Qt 基础】绘图

画笔 QPen pen; pen.setWidth(3); // 线条宽度 pen.setColor(Qt::red);// 画笔颜色 pen.setStyle(Qt::DashLine);// 线条样式 pen.setCapStyle(Qt::RoundCap);// 线端样式 pen.setJoinStyle(Qt::BevelJoin);// 连接样式 painter.setPen(pen);线条 线端 连接 画刷 QBrush bru…