代码随想录 | Day38 | 动态规划 :01背包应用 目标和一和零

代码随想录 | Day38 | 动态规划 :01背包应用 目标和&&一和零

动态规划应该如何学习?-CSDN博客

01背包模板 | 学习总结-CSDN博客

难点:

代码都不难写,如何想到01背包并把具体问题抽象为01背包才是关键


这真的不能怪笔者拖更,这两道题真给我整麻了


文章目录

  • 代码随想录 | Day38 | 动态规划 :01背包应用 目标和&&一和零
    • 494.目标和(恰好等于背包容量求方案数)
      • 思路分析:
      • 1.回溯法
      • 2.01背包
        • 1.回溯暴力枚举
        • 2.记忆化搜索
        • 3.1:1翻译为动态规划
        • 4.滚动数组优化
    • 474.一和零
      • 思路分析:
      • 1.回溯暴力枚举
      • 2.记忆化搜索
      • 3. 1:1翻译为动态规划
      • 4.滚动数组优化

494.目标和(恰好等于背包容量求方案数)

[494. 目标和 - 力扣(LeetCode)](https://leetcode.cn/problems/partition-equal-subset-sum/description/)

思路分析:

设前面要加“+”的数和为p,前面要加“-”的数的和为q。

p+q=sum(数组所有元素的和)

p-q=target(要加正号的减去要加负号的)

2p=sum+target

p=(sum+target)/2

也就是说呢,我们要在nums数组里面找一个子集,让子集的和等于p,能找到几个就有几种方案

1.回溯法

本题也可以使用回溯暴力枚举,直接搜索nums里面的所有组合,等于target的就是答案。

这是组合总和的代码,当然是超时的

class Solution {
private:vector<vector<int>> result;vector<int> path;void backtracking(vector<int>& candidates, int target, int sum, int startIndex) {if (sum == target) {result.push_back(path);}// 如果 sum + candidates[i] > target 就终止遍历for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) {sum += candidates[i];path.push_back(candidates[i]);backtracking(candidates, target, sum, i + 1);sum -= candidates[i];path.pop_back();}}
public:int findTargetSumWays(vector<int>& nums, int S) {int sum = 0;for (int i = 0; i < nums.size(); i++) sum += nums[i];if (S > sum) return 0; // 此时没有方案if ((S + sum) % 2) return 0; // 此时没有方案,两个int相加的时候要格外小心数值溢出的问题int bagSize = (S + sum) / 2; // 转变为组合总和问题,bagsize就是要求的和// 以下为回溯法代码result.clear();path.clear();sort(nums.begin(), nums.end()); // 需要排序backtracking(nums, bagSize, 0, 0);return result.size();}
};

2.01背包

如何转换为01背包呢?

1.因为是子集,所以每个数只有选或者不选两个选项,不会有重复

2.就是把nums里面的数字当做物品,他们的价值和重量全是他们本身的数值

3.而我们背包的容量c就是target这个值,只要我们的方案可以刚好把背包给填满了,那说明背包里面放进去的nums[i]加和就是target

如果不是刚好填满,那肯定找不出子集加起来正好是target

转变为01背包后的递推公式变化?

dp/dfs表示从前 i 个数中从选一些数恰好组成 j 的方案数

就是从前i个数里面随便选,能正号填满容量j的方案数

只需要把01背包的max换成+即可

dp[i][j]=dp[i-1][j]+dp[i-1][j-w[i]];

dp[i][j]是由两个情况相加得到的。我们没有选第i件物品和选了第i件物品

选了那方案数就得加上在容量为j-w[i]的情况下的方案数

没选那方案数就得加上选上一个物品的时候在容量为j的情况下的方案数(上一个物品也分为选或不选)

这里是加法原理,选和不选第i个物品的情况是互斥的,我们要得到所有的方案数量就需要把它加起来

1.回溯暴力枚举

1.参数和返回值

i是物品编号

c是当前容量

nums是题数组

dfs(i,c) 表示从前 i 个数中从选一些数恰好组成 c 的方案数

int dfs(int i,int c,vector<int>& nums)

2.终止条件

1.i<0说明没有物品可以选了,如果当前容量正好等于0,那就返回1说明找到了一个合法方案

不等于0就返回0说明没找到

2.如果当前容量已经小于了物品所需的容量,那说明背包装不下,那就只能不选这个物品了,就返回不选这个物品的方案数量,即在前i-1个物品里面能凑够容量c的方案数

		if(i<0)if(c==0)return 1;elsereturn 0;if(c<nums[i])return dfs(i-1,c,nums);

3.本层逻辑

返回 在前i个物品中,选了第i个物品能满足c的方案数和没选第i个物品能满足c的方案数之和

return dfs(i-1,c-nums[i],nums)+dfs(i-1,c,nums);

完整代码

class Solution {
public:int dfs(int i,int c,vector<int>& nums){if(i<0)if(c==0)return 1;elsereturn 0;if(c<nums[i])return dfs(i-1,c,nums);return dfs(i-1,c-nums[i],nums)+dfs(i-1,c,nums);}int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;return dfs(nums.size()-1,target,nums);}
};
2.记忆化搜索

还是老样子

初始化dp为-1,如果不是-1说明计算过了直接返回

并且在返回之前给dp赋值

class Solution {
public:int dfs(int i,int c,vector<int>& nums,vector<vector<int>>& dp){if(i<0)if(c==0)return 1;elsereturn 0;if(c<nums[i])return dp[i][c]=dfs(i-1,c,nums,dp);if(dp[i][c]!=-1)return dp[i][c];return dp[i][c]=dfs(i-1,c-nums[i],nums,dp)+dfs(i-1,c,nums,dp);}int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;vector<vector<int>> dp(nums.size(),vector<int>(target+1,-1));return dfs(nums.size()-1,target,nums,dp);}
};
//lambda
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;vector<vector<int>> dp(nums.size(),vector<int>(target+1,-1));function<int(int,int)> dfs=[&](int i,int c)->int{if(i<0)if(c==0)return 1;elsereturn 0;if(c<nums[i])return dp[i][c]=dfs(i-1,c);if(dp[i][c]!=-1)return dp[i][c];return dp[i][c]=dfs(i-1,c-nums[i])+dfs(i-1,c);};return dfs(nums.size()-1,target);}
};
3.1:1翻译为动态规划

注意这里把dp数组里面的下标i都加了1(重点)

因为这样做可以避免负数下标的出现

一种好理解的方式是dp[i][j]含义是在前i个物品里面选,容量刚好满足j

物品编号是1-i,而不是0-i

那为什么nums数组的下标没有加+1呢?

因为nums数组从0开始的,dp数组中的i,表示第i件物品,而第i件物品的重量和价值和nums[i-1]

dp[i+1][j]=dp[i][j]+dp[i+1][j-nums[i]];

关于初始化就看上面回溯的终止条件

i<0&&c==0就为1,那里的i是我们这里的i-1,因为我们给dp数组的i下标都加了1

也就是说

dp[i][0]全都为1

但这里只需要把dp[0][0]=1就行,其他的会在循环中给他赋值的,最后打印出来也确实是1

完整代码:

class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;vector<vector<int>> dp(nums.size()+1,vector<int>(target+1,0));dp[0][0]=1;for(int i=0;i<nums.size();i++)for(int j=0;j<=target;j++)if(j<nums[i])dp[i+1][j]=dp[i][j];elsedp[i+1][j]=dp[i][j]+dp[i][j-nums[i]];return dp[nums.size()][target];}
};
4.滚动数组优化
class Solution {
public:int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;vector<int> dp(target+1,0);dp[0]=1;for(int i=0;i<nums.size();i++)for(int j=target;j>=nums[i];j--)dp[j]=dp[j]+dp[j-nums[i]];return dp[target];}
};

474.一和零

474. 一和零 - 力扣(LeetCode)

思路分析:

这道题我也没想到怎么联系到01背包,或者说怎么应用

看了题解以后才了解到了

咱们平时的容量限制是物品i的重量

今天的容量限制是物品i(字符串strs[i])里面的0和1的数量,就是说有两个限制,一个是0的数量,一个是1的数量,说明这是一个三维数组,而每个物品i(字符串strs[i])的价值是多少?

因为求的是子集的数量,所以每一个字符串的价值都为1,就是放这个字符串i进去,就加1,不放就不加

举例:

比如说我们放的放的,背包容量只能装2个0和三个1了

那我们下一个strs[i]如果是 000111,有3个0,3个1那也放不进去

001111也不行

0000也不行

就是两个要都满足

比如0011,然后放进去以后最大的方案数+1

所以本质上就是01背包的容量多加了一个维度

递推公式直接仿照着写

dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
dp[i][j][k] = max(dp[i-1][j][k], dp[i-1][j - zeronum][k - onenum] + 1);

当前的最大数量那就是不放当前字符串和放了当前字符串这两种情况里面选一个最大值,如果我我们选了当前字符串那就+1,因为dp含义是在前i个字符串里面选的最多有j个0和k个1的子集中的字符串数量。

1.回溯暴力枚举

1.参数和返回值

i是物品编号,标识到了第几个字符串了

j是当前0的容量,k是当前1的容量

strs是题数组

dfs(i,j,k,strs) 含义是在前i个字符串里面选的最多有j个0和k个1的子集中的字符串数量

int dfs(int i,int j,int k,vector<string>& strs)

2.终止条件

1.i<0说明没有物品可以选了,如果当前容量正好等于0,那就返回1说明找到了一个合法方案

不等于0就返回0说明没找到

2.如果当前容量已经小于了物品所需的容量,那说明背包装不下,那就只能不选这个物品了,就返回不选这个物品的方案数量,即在前i-1个物品里面能凑够容量c的方案数

if(i<0)return 0;
if(j<zeronum || k<onenum)//0和1只要有一个不够就不放 return dfs(i-1,j,k,strs);

3.本层逻辑

返回当前的最大数量,就是不放当前字符串和放了当前字符串这两种情况里面选一个最大值

		int zeronum=0;int onenum=0;for(auto c:strs[i])if(c=='0')zeronum++;elseonenum++;if(j<zeronum || k<onenum)return dfs(i-1,j,k,strs);return max(dfs(i-1,j,k,strs),dfs(i-1,j-zeronum,k-onenum,strs)+1);

完整代码

当然是超时的

class Solution {
public:int dfs(int i,int c,vector<int>& nums){if(i<0)if(c==0)return 1;elsereturn 0;if(c<nums[i])return dfs(i-1,c,nums);return dfs(i-1,c-nums[i],nums)+dfs(i-1,c,nums);}int findTargetSumWays(vector<int>& nums, int target) {int sum=0;for(int c:nums)sum+=c;int p=sum+target;if (p < 0 || p % 2 != 0)return 0;target=p/2;return dfs(nums.size()-1,target,nums);}
};

2.记忆化搜索

还是老样子

初始化dp为-1,如果不是-1说明计算过了直接返回

并且在返回之前给dp赋值

class Solution {
public:int dfs(int i,int j,int k,vector<string>& strs,vector<vector<vector<int>>>& dp){if(i<0)return 0;int zeronum=0;int onenum=0;for(auto c:strs[i])if(c=='0')zeronum++;elseonenum++;if(dp[i][j][k]!=-1)return dp[i][j][k];if(j<zeronum || k<onenum)return dp[i][j][k]=dfs(i-1,j,k,strs,dp);return dp[i][j][k]=max(dfs(i-1,j,k,strs,dp),dfs(i-1,j-zeronum,k-onenum,strs,dp)+1);}int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<vector<int>>> dp(strs.size(),vector<vector<int>>(m+1,vector<int>(n+1,-1)));return dfs(strs.size()-1,m,n,strs,dp);}
};
//lambda
虚晃一枪,笔者懒得写了,大家自己写吧

3. 1:1翻译为动态规划

多加了初始化部分,就是物品1,能放下的地方初始化为1,放不下的初始化为0

		int zeronum=0;int onenum=0;for(auto c:strs[0])if(c=='0')zeronum++;elseonenum++;for(int j=0;j<=m;j++)for(int k=0;k<=n;k++)if(j<zeronum||k<onenum)dp[0][j][k]=0;elsedp[0][j][k]=1;

完整代码:

class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<vector<int>>> dp(strs.size(),vector<vector<int>>(m+1,vector<int>(n+1,0)));int zeronum=0;int onenum=0;for(auto c:strs[0])if(c=='0')zeronum++;elseonenum++;for(int j=0;j<=m;j++)for(int k=0;k<=n;k++)if(j<zeronum||k<onenum)dp[0][j][k]=0;elsedp[0][j][k]=1;for(int i=1;i<strs.size();i++){zeronum=0;onenum=0;for(auto c:strs[i])if(c=='0')zeronum++;elseonenum++;for(int j=0;j<=m;j++)for(int k=0;k<=n;k++)if(j<zeronum||k<onenum)dp[i][j][k]=dp[i-1][j][k];elsedp[i][j][k]=max(dp[i-1][j][k],dp[i-1][j-zeronum][k-onenum]+1);}return dp[strs.size()-1][m][n];}
};

4.滚动数组优化

和01背包一样,先遍历物品

容量倒着遍历

删掉物品编号那一维

除了初始化为0不需要其他的初始化动作

class Solution {
public:int findMaxForm(vector<string>& strs, int m, int n) {vector<vector<int>> dp(m+1,vector<int>(n+1,0));for(int i=0;i<strs.size();i++){int zeronum=0;int onenum=0;for(auto c:strs[i])if(c=='0')zeronum++;elseonenum++;for(int j=m;j>=zeronum;j--)for(int k=n;k>=onenum;k--)dp[j][k]=max(dp[j][k],dp[j-zeronum][k-onenum]+1);}return dp[m][n];}
};

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

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

相关文章

巡检任务管理系统(源码+文档+部署+讲解)

本文将深入解析“巡检任务管理系统”的项目&#xff0c;探究其架构、功能以及技术栈&#xff0c;并分享获取完整源码的途径。 系统概述 巡检任务管理、巡检抽查、巡检任务随机分派等功能 本项目名称为巡检管理系统&#xff0c;是对巡检工作进行数字化管理的系统。该系统适用…

RK3288 android7.1 适配 ilitek i2c接口TP

一&#xff0c;Ilitek 触摸屏简介 Ilitek 提供多种型号的触控屏控制器&#xff0c;如 ILI6480、ILI9341 等&#xff0c;采用 I2C 接口。 这些控制器能够支持多点触控&#xff0c;并具有优秀的灵敏度和响应速度。 Ilitek 的触摸屏控制器监测屏幕上的触摸事件。 当触摸发生时&am…

Windows系统中Oracle VM VirtualBox的安装

一.背景 公司安排了师带徒&#xff0c;环境搭建问题一直是初级程序员头疼的事情&#xff0c;我记录一下这些基础的内容&#xff0c;方便初学者。大部分开发者的机器还是windows系统&#xff0c;所以写了怎么安装。 二.版本信息及 操作系统&#xff1a;windows11 家庭版…

HTTP的了解

从输入 URL 到页面展示到底发生了什么&#xff1f;&#xff08;非常重要&#xff09; 类似的问题&#xff1a;打开一个网页&#xff0c;整个过程会使用哪些协议&#xff1f; 先来看一张图&#xff08;来源于《图解 HTTP》&#xff09;&#xff1a; 上图有一个错误需要注意&…

2-149 基于matlab的LDPC译码性能分析

基于matlab的LDPC译码性能分析&#xff0c;LDPC&#xff08;Low-Density Parity-Check&#xff09;码作为编码技术&#xff0c;具有优秀的纠错性能和较低的编解码复杂度。为保证可靠的数据传输&#xff0c;对传输过程中可能出现的信道噪声、干扰等进行模拟和分析。分析对比了误…

医学可视化之热力图

在医学领域&#xff0c;热力图是另一种非常有用的可视化工具&#xff0c;它能够以独特的方式展示数据的密度和趋势。 一、热力图的特点 热力图是一种通过颜色变化来表示数据密度或趋势的可视化图表。它通常将数据值映射到不同的颜色区间&#xff0c;颜色越深表示数据值越高&a…

YOLOv11融合[ECCV2024]自调制特征聚合SMFA模块及相关改进思路|YOLO改进最简教程

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《SMFANet: A Lightweight Self-Modulation Feature Aggregation Network for Efficient Image Super-Resolution》 一、 模块介绍 论文链接&#xff1…

【C++】C++移动语义、左值右值、左值引用右值引用、移动构造函数、std::move、移动赋值操作符

二十五、C移动语义、左值和右值、左值引用右值引用、移动构造函数、std::move、移动赋值操作符 本部分讨论一些更高级的C特性&#xff1a;C移动语义。但是讲移动语义之前我们得先了解什么左值右值、左值引用和右值引用。 1、C的左值和右值、左值引用和右值引用左值是有地址的…

三菱QD77MS定位模块速度更改功能

速度更改功能” 是以任意时机将控制中的速度更改为新指定的速度的功能。更改后的速度直接设置到缓冲存储器中&#xff0c;并根据速度更改指令([cd.15速度更改请求)或者外部指令信号执行速度更改。 但是&#xff0c;机械原点复位的情况下&#xff0c;检测出近点狗 ON 并开始向蠕…

【Django】视图函数

【Django】视图函数 视图函数的本质是Python中的函数&#xff0c;视图函数负责处理用户的请求并返回响应&#xff0c;该响应可以是网页的HTML内容、重定向、404错误、XML文档、图像或者任何东西&#xff0c;一般在应用中的views.py编写&#xff0c;示例代码如下&#xff1a; …

Git 入门篇(二)

前言 Git 入门篇&#xff08;一&#xff09; Git 入门篇&#xff08;二&#xff09; Git 入门篇&#xff08;三&#xff09; 目录 创建远程代码仓库 创建本地代码仓库 同步本地-远程代码仓库 代码托管 创建远程代码仓库 登录&#xff1a;gitee.com ​ 新建仓库 ​ 创建本…

PLC_博图系列☞基本指令”TOF:启动关断延时定时器“

PLC_博图系列☞基本指令”TOF&#xff1a;启动关断延时定时器“ 文章目录 PLC_博图系列☞基本指令”TOF&#xff1a;启动关断延时定时器“背景介绍TOF&#xff1a; 启动关断延时定时器说明参数脉冲时序图示例 关键字&#xff1a; PLC、 西门子、 博图、 Siemens 、 TOF 背…

【RabbitMQ】之高可用集群搭建

一、RabbitMQ 集群简介 1、默认集群原理1-1、RabbitMQ 集群简介 单台 RabbitMQ 服务器处理消息的能力是有瓶颈的&#xff0c;而且可靠性还无法保证&#xff0c;所以需要通过集群来提高消息的吞吐量和提高数据可靠性。 由于 RabbitMQ 本身是基于 Erlang 编写&#xff0c;而 Er…

改进系列(3):基于ResNet网络与CBAM模块融合实现的生活垃圾分类

目录 1. ResNet介绍 2. CBAM 模块 3. resnet cbam 3.1 添加在每个layer层后 3.2 关于训练的建议 4. 垃圾分类实战 4.1 数据集 4.2 训练 4.3 最好的权重 4.4 推理 5. 其它 1. ResNet介绍 ResNet&#xff08;残差网络&#xff09;是一种深度卷积神经网络模型&#xf…

Linux 服务器上部署 .NET Core 应用程序,值得收藏!

在 Linux 服务器上部署 .NET Core 应用程序&#xff0c;标志着传统的以微软为中心的部署平台的重大转变。.NET Core 的跨平台特性允许开发人员享受 Linux 环境的性能、可靠性和安全性。本指南提供了在各种 Linux 发行版上部署 .NET Core 应用程序的全面概述&#xff0c;重点是使…

2024-11-01 - 统一身份认证 - OpenLdap - 中间件 - 流雨声

摘要 2024-11-01 周五 杭州 暴雨 调查问卷: https://www.wjx.cn/vm/exIBFDM.aspx# 2024年转瞬即逝&#xff0c;可是生活还在继续&#xff0c;这里有一项关于人工智能和项目管理对于效能关系的调研问卷&#xff0c;AI 对工作的作用和影响。问卷不采集个人信息&#xff0c;在此…

前端页面性能优化的常见问题与解决方案

在当今互联网高速发展的时代&#xff0c;前端页面的性能对于用户体验至关重要。一个加载缓慢、交互卡顿的页面很可能会导致用户流失。本文将深入探讨前端页面性能优化中常见的问题以及相应的解决方案。 一、常见问题 &#xff08;一&#xff09;资源加载问题 文件体积过大 …

视频播放相关的杂记

基于QT FFMPEG设计一款 RTMP协议推流、视频录制软件 实现的功能&#xff1a; &#xff08;1&#xff09;将摄像头视频流 麦克风音频流合并&#xff0c;并推到流媒体服务器 &#xff08;2&#xff09;将摄像头视频流 麦克风音频流保存到本地磁盘 基于QtFFMPEG设计一款RTM…

Pycharm,2024最新版Pycharm下载安装配置教程!

目录 1、Pycharm 简介2、Pycharm下载3、环境变量的配置4、Pycharm的使用 1、Pycharm 简介 Pycharm资料领取不收米 PyCharm是一种Python IDE&#xff08;Integrated Development Environment&#xff0c;集成开发环境&#xff09;&#xff0c;带有一整套可以帮助用户在使用Py…

Redis9:商户查询缓存3

欢迎来到“雪碧聊技术”CSDN博客&#xff01; 在这里&#xff0c;您将踏入一个专注于Java开发技术的知识殿堂。无论您是Java编程的初学者&#xff0c;还是具有一定经验的开发者&#xff0c;相信我的博客都能为您提供宝贵的学习资源和实用技巧。作为您的技术向导&#xff0c;我将…