代码随想录-训练营-day30

news2025/2/23 4:43:30

今天我们要进入动态规划的背包问题,背包问题也是一类经典问题了。总的来说可以分为:

今天让我们先来复习0-1背包的题目,这也是所有背包问题的基础。所谓的0-1背包问题一般来说就是给一个背包带有最大容量,然后给一个物体对应的需要的容量和价格的表格,且每个物体只能取一次,问我们怎么得到最大价值。

我们依然使用动态规划的既定套路来做:

第一步,确定我们的dp数组的含义,首先明确的是:我们需要一个二维的dp数组,因为我们需要同时表示背包的容量和物体,也就是dp[i][j]表示的是容量为j的背包中遍历到下标为i的物体时的最大价值。

第二步,递推公式,我们可以设想一下dp[i][j]可以怎么由之前的值得到,对于容量为j的背包来说,物体i有两种情况:放或者不放,不放的话就是dp[i-1][j],放的话就是dp[i-1][j-weights[i]]+values[i](要放下标为i的物体需要在背包中预留出他的容量),所以递推公式就是:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weights[i]]+values[i])

第三步,初始化,我们要如何初始化我们的dp数组呢?显然dp[0][0]=0(容量为0),事实上,所有容量为0的背包的初始值都应该是0,也就是dp[i][0]=0;那么反过来的dp[0][j]呢?这个就要看我们下标为0的物品重量与我们的j的关系了,如果大于j显然也是0,如果小于j的话那就可以是values[0]。那么dp[0][j]应该就是从j=weights[0]开始,遍历到最后。

最后的初始化结果如下:

遍历顺序的话,也很有讲究,我们是该先遍历物品呢,还是该先遍历背包呢?

答案其实是,对于0-1背包来说,都可以。

我们拿一个图来说明:

由我们的递推公式已知,我们的dp[i][j]的更新都是依靠左上角的值来更新的,那么我们先遍历背包再遍历物品就是先横向再纵向,反之是先纵向再横向,但最后我们都是往右下角推进的,都是符合要求的,所以对于这个题来说,先遍历哪个都是可取的。

46. 携带研究材料(第六期模拟笔试) (kamacoder.com)

是的小明是一位科学家但是不重要,因为这就是一个标准的动态规划的背包问题,让我们开始做吧。

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,zooms;
    cin>>n>>zooms;
    vector<int> weights(n);
    for(int i=0;i<n;++i){
        cin>>weights[i];
    }
    vector<int> values(n);
    for(int i=0;i<n;++i){
        cin>>values[i];
    }
    vector<vector<int>> dp(n,vector<int>(zooms+1,0));
    for(int j=weights[0];j<=zooms;++j){
        dp[0][j]=values[0];
    }
    for(int i=1;i<n;++i){
        for(int j=0;j<=zooms;++j){
            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]]+values[i]);
        }
    }
    cout<<dp[n-1][zooms]<<endl;
    return 0;
}

这个题需要注意的一点是:我们讨论物品的时候是根据下标来的,是从0开始的,也就是物品有n种,但是我们遍历只遍历到n-1;而容量是从1开始的,也就是我们得从1遍历到具体的容量值,而同样的我们返回的也得是dp[n-1][zooms]才可。

虽然我们的分析已经完成,但是并非没有可以改善之处。比如我们其实不难发现我们的现在的遍历的j都是从0开始,去判断当前j和weights[i]的关系。但实际上,我们还有更聪明的做法:

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weights[i]]+values[i])这是我们的递推公式,但是我们可以注意到的是,dp[i-1][j]就是位于dp[i][j]的正上方,也就是说其实我们更多地只是在看我们的另一项,否则我们会直接继承dp[i-1][j],那么这个时候我们就可以选择不用i来记录每一个这个数,而是将i这个维度去掉来不断更新我们的dp[j]达到相同的效果:

这样我们的递推公式就变成了:dp[j]=max(dp[j],dp[j-weights[i]]-values[i])。可以看到我们的数组一下就变成了一个一维数组,这样无疑大大减小我们的空间复杂度,也更清晰明了。

让我们再重复一遍我们的动态规划的步骤:

首先确定dp数组的含义,对于一维数组dp[j]来说,显然dp[j]代表背包容量为j时能填充的最大价值。

递推公式,已知是dp[j]=max(dp[j],dp[j-weights[i]]-values[i])。

然后是初始化,对于dp[0]来说,显然等于0。

遍历顺序,这个是背包问题中最值得讲究的点:我们一般来说都是循环的顺序都是先遍历物品再遍历背包容量,这个在一维数组的情况也适用。然后是我们关于物品和背包的遍历方式,物品我们依然还是从下标为0开始遍历到n-1,但是我们的背包容量遍历就必须要反过来,也就是:

for(int j=zooms;j>=weights[i];--j)为什么呢?因为我们的dp[j]是完全由dp[j]与dp[j-weights[i]]+values[i]来递推的,对于i来说并没有专门记录,如果从0开始遍历到zooms,我们很可能会反复添加一个物品(比如背包容量为2,可以装两次重量为1的物体),导致违背了0-1背包的一个物体只能使用一次的原则。而反着来遍历的话,我们其实是从大往小遍历,只要题目设计合理,我们就可以避免这个问题。

让我们再用一维数组的方式做一遍携带研究材料吧:

#include<iostream>
#include<vector>
using namespace std;
int main(){
    int n,zooms;
    cin>>n>>zooms;
    vector<int> weights(n);
    for(int i=0;i<n;++i){
        cin>>weights[i];
    }
    vector<int> values(n);
    for(int i=0;i<n;++i){
        cin>>values[i];
    }
    vector<int> dp(zooms+1,0);
    for(int i=0;i<n;++i){
        for(int j=zooms;j>=weights[i];--j){
            dp[j]=max(dp[j],dp[j-weights[i]]+values[i]);
        }
    }
    cout<<dp[zooms]<<endl;
    return 0;
}

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

这个题乍一看可能还挺难想到是用动态规划来做,但是随着我们的思路慢慢一路顺延其实很自然地就能想到。

首先我们要求将数组分割成两个元素和相等的子集,那么我们的第一层尝试自然是求数组的总和后整除2,如果都不能整除成功就可以直接返回false。然后就是我们需要尝试将数组的一个个元素塞进一个数组中,要求这个数组的和为我们大数组的总和的一半。是不是有一种既视感?是的,其实我们的容量就是这个总和的一半,大数组里的数字就是我们的value。

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

第一步:dp数组的含义:dp[i]代表容量为i的背包中能包含的最大和。

第二步:递推公式:dp[i]=max(dp[i],dp[i-nums[i]]+nums[i]),这里我们的物体的重量和价值是一样的。但是重量是重量,价值是价值,概念上不可以混淆。

第三步,初始化,全部初始化为0即可。

第四步,确定递推顺序,这个我们在之前的一维数组部分已经讲解了。

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

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

相关文章

全平台搭载旭日5!科沃斯GOAT智能割草机器人全新系列正式开售

要闻 近日&#xff0c;科沃斯全新发布的GOAT A Series 和 GOAT O Series割草机器人&#xff0c;将在多国市场正式上市发售。作为业界最强的割草机器人产品之一&#xff0c;GOAT致力为割草机带来基于机器人视觉的专业定位解决方案。科沃斯GOAT全新系列产品全平台搭载地瓜机器人…

自定义组件数据监听器案例,纯数据字段,自定义组件生命周期,页面的生命周期,插槽

1.自定义组件数据监听器案例 1.1基础案例模板 1.2定义button事件的处理函数 1.3监听对象中属性的变化&#xff0c;并且为fullColor赋值 使用通配符监听所有属性变化 2.自定义组件的纯数据字段 、 3.自定义组件的生命周期 4.组件所在页面的生命周期 5.自定义组件插槽 5.1单个插…

mybatis-lombok工具包介绍

Lombok是一个实用的]ava类库&#xff0c;能通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法&#xff0c;并可以自动化生成日志变量&#xff0c;简化java开发、提高效率。 使用前要加入Lombok依赖

LDO技术:线性调整率与负载调整率全解析

LDO(Low Dropout Regulator)低压差线性稳压器&#xff0c;其结构比较简单、纹波和噪声比DCDC小、成本也优于DCDC&#xff0c;缺点是在输入电压和输出电压的压差比较大时&#xff0c;效率低些&#xff0c;但在小电流电源电路上被广泛使用。现在输入电压和输出电压的压差可做到10…

SpringBoot 集成 Caffeine 实现本地缓存

目录 1、Caffeine 简介 1.1、Caffeine 简介1.2、对比 Guava cache 的性能主要优化项1.3、常见的缓存淘汰算法1.4、SpringBoot 集成 Caffeine 两种方式 2、SpringBoot 集成 Caffeine 方式一 2.1、缓存加载策略 2.1.1、手动加载2.1.2、自动加载【Loading Cache】2.1.3、异步加载…

使用EVE-NE-锐捷实现NAT+ACL服务限制

一、项目拓扑 二、项目实现 1.NET配置 点击左侧的NetWorks,设置与图相同的配置&#xff0c;实现实验环境桥接到物理网络 2.GW配置 进入特权模式 enable进入全局模式 configure terminal 更改名称为GW hostname GW进入g0/0接口 interface g0/0将g0/0接口IP地址配置为192.168.…

大模型训练为什么依赖GPU

近年来&#xff0c;随着人工智能技术的飞速发展&#xff0c;特别是深度学习领域的进步&#xff0c;大模型的训练逐渐成为研究和工业界的热点。作为大模型训练中的核心硬件&#xff0c;GPU&#xff08;图形处理单元&#xff09;扮演了至关重要的角色。那么&#xff0c;为什么大模…

二叉树链式结构:数据结构中的灵动之舞

目录 前言 一、 前置说明 二、二叉树的遍历 2.1前序遍历 2.2中序遍历 2.3 后序遍历 2.4层序遍历 三、二叉树的遍历的应用 3.1二叉树节点个数&#xff1a; 3.2二叉树的高度 3.3 二叉树第k层的节点的个数 3.4二叉树的查找 总结 前言 在数据结构的世界里&#xff0c;二叉…

新建github操作

1.在github.com的主页根据提示新建一个depository。 2.配置用户名和邮箱 git config --global user.name "name" git config --global user.email "email" 3.生成ssh秘钥 ssh-keygen -t rsa 找到public key 对应的文件路径 cat /root/.ssh/id_rsa 复制显…

第 15 天:数据存储,打造存档 读取系统!

&#x1f3af; 目标&#xff1a; ✅ 掌握 UE5 SaveGame 存档系统 ✅ 在 C 创建存档类&#xff0c;存储游戏数据 ✅ 实现存档 & 读取功能&#xff0c;让游戏状态可持久化 ✅ 在 BP_PlayerCharacter 里实现&#xff1a; * 游戏开始时自动加载存档 * 玩家受到伤害时自动存档 …

Flutter 异步编程利器:Future 与 Stream 深度解析

目录 一、Future&#xff1a;处理单次异步操作 1. 概念解读 2. 使用场景 3. 基本用法 3.1 创建 Future 3.2 使用 then 消费 Future 3.3 特性 二、Stream&#xff1a;处理连续异步事件流 1. 概念解读 2. 使用场景 3. 基本用法 3.1 创建 Stream 3.2 监听 Stream 3.…

Java短信验证功能简单使用

注册登录阿里云官网&#xff1a;https://www.aliyun.com/ 搜索短信服务 自己一步步申请就可以了 开发文档&#xff1a; https://next.api.aliyun.com/api-tools/sdk/Dysmsapi?version2017-05-25&languagejava-tea&tabprimer-doc 1.引入依赖 <dependency>…

React进阶之React核心源码解析(一)

React核心源码解析 react 特点CPU卡顿IO 卡顿 新老 react 架构对比v15v16.8Scheduler 调度器Reconciler 协调器 React fiber原理更新dommount 构建过程 render阶段 — scheduler reconcilerreact源码解析react-domreact-dom/src/client/ReactDOMRoot.js react-reconcilerreact-…

【Vue】打包vue3+vite项目发布到github page的完整过程

文章目录 第一步&#xff1a;打包第二步&#xff1a;github仓库设置第三步&#xff1a;安装插件gh-pages第四步&#xff1a;两个配置第五步&#xff1a;上传github其他问题1. 路由2.待补充 参考文章&#xff1a; 环境&#xff1a; vue3vite windows11&#xff08;使用终端即可&…

类加载机制及双亲委派模型

一、引言 二、类加载流程 1. 加载 2. 连接 2.1 验证 2.2 准备 2.3 解析 3. 初始化 三、类加载器 类加载器的类型 双亲委派模型 打破双亲委派模型 双亲委派模型优点 一、引言 在 Java 的运行机制中&#xff0c;类加载是一个至关重要的环节。它不仅决定了 Java 程序的动态…

尚硅谷课程【笔记】——大数据之Hadoop【一】

课程视频链接&#xff1a;尚硅谷Hadoop3.x教程 一、大数据概论 1&#xff09;大数据概念 大数据&#xff08;Big Data&#xff09;&#xff1a;指无法再一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发…

DockerFile优化镜像体积

title: DockerFile优化镜像体积 date: 2025-02-15 15:22:40 tags: DockerFile优化镜像体积DockerFile优化镜像体积 DockerFile优化镜像体积前文回顾:一、细数优化镜像体积的思路与方式二、优化Dockfile文件编辑 Dockerfile2文件三、构建镜像四、运行镜像五、查看运行效果原文 …

题解:洛谷 P4113 [HEOI2012] 采花

题目https://www.luogu.com.cn/problem/P4113 运用类似于P1972 [SDOI2009] HH的项链的操作&#xff0c;将数据离线下来处理。 按照区间右端点从小到大排序。 问题是数量大于等于 的时候才能算进去。 于是乎我们用两个数组维护倒数第二次出现和最后一次出现的地方。 每次在…

easyexcel快速使用

1.easyexcel EasyExcel是一个基于ava的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel 即通过java完成对excel的读写操作&#xff0c; 上传下载 2.easyexcel写操作 把java类中的对象写入到excel表格中 步骤 1.引入依赖 <depen…

android studio下载安装汉化-Flutter安装

1、下载android studio官方地址&#xff1a;&#xff08;这个网址可能直接打不开&#xff0c;需要VPN&#xff09; https://developer.android.com/studio?hlzh-cn mac版本分为X86和arm版本&#xff0c;电脑显示芯片是Inter的就是x86的&#xff0c;显示m1和m2的就是arm的 …