代码随想录算法训练营day41

news2024/11/20 23:38:49

题目:01背包理论基础、416. 分割等和子集

参考链接:代码随想录

动态规划:01背包理论基础

思路:01背包是所有背包问题的基础,第一次看到比较懵,完全不知道dp数据怎么设置。具体分析还是dp五部曲,首先是dp数据,先想本题需要求的内容,即给定背包容量下,求能放置物品的最大价值和,因此想到的数据存放内容肯定是最大价值和,但本题有容量和物品两个维度,而之前的题目都只有一个维度,故需要用到二维数据,dp[i][j]表示从下标0-i物品中选择任意物品放入容量为j的背包中,能得到的最大价值和
在这里插入图片描述
然后第二步递推公式,要递推考虑第i个物品,有两种情况,首先是不放i物品,这时候dp[i][j]=dp[i-1][j],与前i-1个物品的结果相同,然后是放i物品,这时候背包还剩下的重量为j-weight[i],然后在这些重量中放置前i-1个物品,dp[i][j]=dp[i-1][j-weight[i]]+value[i],最终递推即在这两个里取最大值,如果物品i本来就放不进去,那么就只有第一种情况;第三部dp初始化,首先是j=0的时候,背包放不了任何东西,故dp[i][0]=0,然后是i=0即只有物品0的时候,这时候就是看背包能不能放下weight[0],如果放得下则为weight[0],否则为0,其他都被初始化为0;第四步为确定遍历顺序,先遍历物品和背包都可以,因为都是左上推右下,我们就先遍历物品;举例略。时间复杂度O(mn)。

#include<iostream>
#include<cstring>
using namespace std;

int maxValue(int n,int m,int space[],int value[]){
    int dp[m][n+1];//空间需要0-n,物品从0-m-1即可
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++){//初始化物品0
        if(space[0]<=i){//物品0可以放进背包了
            dp[0][i]=value[0];
        }
    }
    for(int i=1;i<m;i++){
        for(int j=1;j<=n;j++){
            if(space[i]>j){//物品i无法先放进去
                dp[i][j]=dp[i-1][j];
            }
            else{//先放物品i
                dp[i][j]=max(dp[i-1][j],dp[i-1][j-space[i]]+value[i]);
            }
        }
    }
    return dp[m-1][n];
}

int main(){
    int n,m;
    while(cin >> m >> n){
        
        int space[m],value[m];
        for(int i=0;i<m;i++){
            cin >> space[i];
        }
        for(int i=0;i<m;i++){
            cin >> value[i];
        }
        cout << maxValue(n,m,space,value) << endl;
        
    }
    return 0;
}

我比较喜欢的ACM写法是把解题过程抽象成一个函数,数据输入全部在main中完成,标答是在solve()中处理输入。

看标答发现可以把数据压缩成一维,因为递推公式的i都是i-1,我们可以只保留每一行,也就是滚动数组。新的一维dp数组dp[j]表示容量为j的背包能装入的最大价值;递归公式,还是看物品i能不能先放进去,如果不能,那么dp[j]就不变,如果能放先放进去,那么就比较当前dp[j]和dp[j-weight[i]]+value[i],取最大值;初始化,只初始化一行,dp[0]肯定为0,其他下标位置,也初始化为0,因为后面递推过程中会逐渐取最大值;遍历顺序,这是一维写法比较困难的地方,必须从背包容量从大到小遍历,因为如果从小到大,会把前面的背包放入两次,比如w[0]=1,v[0]=15,在计算dp[1]的过程中即为dp[0]+v[0]=15,而对dp[2],如果取dp[1]+value[0]=30,会算两次物品0,而从大到小遍历,我们一开始没有放入物品,dp[0]初始化均为0,dp[2]=dp[1]+value[0]=15,因为在这一次遍历中,还没有考虑到dp[1],后续计算dp[1]=dp[0]+value[0]=15,故不会重复放入,而之前的二维数组,每一次dp[i][j]都由上一层计算而来,本层的没有被覆盖过,因此不存在重复计算问题;举例略。主要就是为了避免本层覆盖的问题,因为dp数组都是由上一层计算的。一维滚动数组写法的代码如下:

int maxValue(int n,int m,int space[],int value[]){
    int dp[n+1]={0};
    for(int i=1;i<=n;i++){//初始化
        if(space[0]<=i){//物品0可以放进背包了
            dp[i]=value[0];
        }
    }
    for(int i=1;i<m;i++){
        for(int j=n;j>=0;j--){
            if(space[i]<=j){//物品i能先放进去
                dp[j]=max(dp[j],dp[j-space[i]]+value[i]);
            }
        }
    }
    return dp[n];
}

真正理解需要画图分析。一维写法空间复杂度大幅下降,需要掌握。

416. 分割等和子集

思路:本题的第一想法就是使用回溯暴力搜索和为sum/2的所有元素,如果找到返回true。能不能套用01背包问题呢,把数组中所有元素对应为物品,每个物品只能放入一次,背包的容量为sum/2,放入的物品重量为元素的数值,价值也为元素的数值,需要保证背包正好装满。对应dp五部曲,首先是dp数组,dp[j]表示背包容量为j时,能放入最大价值,若背包容量为target,则dp[target]即背包装满后的容量,dp[target]=target即装满,如果装不满,即价值未达到要求,dp[target]<target;递推公式,物品i能放进去的时候,dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]),因为物品i的weight和value都是nums[i];初始化,由于题目的价值都是非负,dp直接全初始化为0即可;递推顺序,按重量从大到小;举例略。时间复杂度O(n^2)。

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum=accumulate(nums.begin(),nums.end(),0);//库函数求和
        if(sum%2==1){//和为奇数直接排除
            return false;
        }
        int target=sum/2;
        vector<int> dp(20001,0);//由题意知最大的和不会超过20000
        int n=nums.size();
        for(int i=0;i<n;i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        }
        if(dp[target]==target){//容量为target的背包装完后价值恰好为target,即表示装满
            return true;
        }
        return false;
    }
};

本题的难点就在于将原问题和01背包对应起来,主要是装满如何想到,即把价值和重量都定义成元素的值,装满target重量即为target容量的背包能装的最大价值恰好为target,即dp[target]=target。

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

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

相关文章

Vue3实战笔记(58)—从零开始掌握Vue3插槽机制,基础入门

文章目录 前言插槽基础入门总结 前言 不论是组件封装还是分析源码&#xff0c;实际开发中经常接触插槽&#xff0c;插槽是干什么用的呢&#xff1f;组件之间能够接收任意类型的 JavaScript 值作为 props&#xff0c;但组件要如何接收模板内容呢&#xff1f;在某些场景中&#…

openssl 常用命令demo

RSA Private Key的结构&#xff08;ASN.1&#xff09; RSAPrivateKey :: SEQUENCE { version Version, modulus INTEGER, -- n publicExponent INTEGER, -- e privateExponent INTEGER, -- d prime1 INTEGER, -- …

k8s学习--ConfigMap详细解释与应用

文章目录 一 什么是configmapConfigMap 的好处ConfigMap 的限制 二.创建ConfigMap的4种方式1.在命令行指定参数创建2.在命令行通过多个文件创建3.在命令行通过文件提供多个键值对创建4.YAML资源清单文件创建 三 configmap的两种使用方法1.通过环境变量的方式传递给pod2.通过vol…

vue3+typescript 使用Codemirror

安装 // npm npm install codemirror-editor-vue3 codemirror^5.65.12// ts版 还需安装&#xff1a; npm install types/codemirror全局注册 修改main.ts&#xff1a; import { createApp } from vueimport App from ./App.vueimport { InstallCodemirro } from "code…

文件编码概念

文件的读取 open()函数&#xff1a; 打开一个已存在的文件&#xff0c;或者创建一个新文件 open(name,mode,encoding) name:是要打开的目标文件名的字符串&#xff08;可以包含文件所在的具体路径&#xff09; mode:设置打开文件的模式&#xff08;访问模式&#xff09;&am…

LabVIEW步进电机的串口控制方法与实现

本文介绍了在LabVIEW环境中通过串口控制步进电机的方法&#xff0c;涵盖了基本的串口通信原理、硬件连接步骤、LabVIEW编程实现以及注意事项。通过这些方法&#xff0c;用户可以实现对步进电机的精确控制&#xff0c;适用于各种自动化和运动控制应用场景。 步进电机与串口通信…

【Linux】信号(一)

信号我们将从信号产生&#xff0c;信号的保存&#xff0c;信号处理分别进行讲解~ 至少大思路是这样。开始之前还要进行一些基础知识的铺垫。 目录 从生活中提炼一些结论&#xff1a;信号概念的一些储备&#xff1a;信号产生&#xff1a;一、kill指令&#xff1a;二、键盘组合键…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式439张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;439 标注数量(xml文件个数)&#xff1a;439 标注数量(txt文件个数)&#xff1a;439 标注类别…

面试官:核心线程数为零时,线程池会处理任务吗?

程序员的公众号&#xff1a;源1024&#xff0c;获取更多资料&#xff0c;无加密无套路&#xff01; 最近整理了一波电子书籍资料&#xff0c;包含《Effective Java中文版 第2版》《深入JAVA虚拟机》&#xff0c;《重构改善既有代码设计》&#xff0c;《MySQL高性能-第3版》&…

Redis篇 list类型在Redis中的命令操作

list在redis基本的命令 一.基本命令1.lpush和range2.lpushx rpushx3.lpop rpop4.lindex linsert llen5.lrem6.ltrim lset7.blpop brpop 一.基本命令 list在redis中相当于数组或者顺序表. 1.lpush和range 2.lpushx rpushx 3.lpop rpop 4.lindex linsert llen 如果要插入的列表中…

详解 Spark 核心编程之累加器

累加器是分布式共享只写变量 一、累加器功能 ​ 累加器可以用来把 Executor 端的变量信息聚合到 Driver 端。在 Driver 程序中定义的变量&#xff0c;在 Executor 端的每个 Task 都会得到这个变量的一份新的副本&#xff0c;每个 task 更新这些副本的值后&#xff0c;传回 Dri…

程序媛:拽姐

更多精彩内容在公众号。 最近都在玩梗图&#xff0c;我也来玩下拽姐的梗图。来说说拽姐做为程序媛的痛。 程序媛的痛不在于996&#xff0c;而在于无休止的攻关。拽姐刚入职听领导说攻关不多&#xff0c;一年也就一次&#xff0c;拽姐心中暗喜&#xff0c;觉得来对了地方。结果…

MySQL之查询性能优化(六)

查询性能优化 查询优化器 9.等值传播 如果两个列的值通过等式关联&#xff0c;那么MySQL能够把其中一个列的WHERE条件传递到另一列上。例如&#xff0c;我们看下面的查询: mysql> SELECT film.film_id FROM film-> INNER JOIN film_actor USING(film_id)-> WHERE f…

百度地图API 教程使用 嵌套到vue3项目中使用,能够定位并且搜索地点名称位置,反向解析获取经度和维度

文章目录 目录 文章目录 流程 小结 概要安装流程技术细节小结 概要 注册百度地图成为开发者&#xff1a; 登录百度账号 注册成功开始下一步 百度地图API是百度提供的一组开发接口&#xff0c;用于在自己的应用程序中集成地图功能。通过百度地图API&#xff0c;您可以实现地图…

1Panel 搭建 halo博客

线上服务器一直闲置&#xff0c;刷到视频 1Panel 能更好管理服务器&#xff0c;还能快速搭建博客&#xff0c;便上手试试&#xff0c;的确很方便&#xff0c;顺手记录一下。 零、准备工作 一台服务器&#xff08;按需购买&#xff0c;此处准备的阿里云服务器一台&#xff0c;也…

负载均衡算法深度探析:F5技术在高效流量管理中的应用

传统的单一服务器模式下&#xff0c;随着用户请求量的增加&#xff0c;单个服务器可能会承受过重的压力&#xff0c;导致响应速度下降甚至系统崩溃&#xff0c;负载均衡技术应运而生。它广泛应用于各种软硬件系统中&#xff0c;将网络流量以某种算法合理分配给各个节点&#xf…

电阻、电容和电感测试仪设计

在现代化生产、学习、实验当中,往往需要对某个元器件的具体参数进行测量,在这之中万用表以其简单易用,功耗低等优点被大多数人所选择使用。然而万用表有一定的局限性,比如:不能够测量电感,而且容量稍大的电容也显得无能为力。所以制作一个简单易用的电抗元器件测量仪是很…

鸿蒙轻内核M核源码分析系列七 动态内存Dynamic Memory

内存管理模块管理系统的内存资源&#xff0c;它是操作系统的核心模块之一&#xff0c;主要包括内存的初始化、分配以及释放。 在系统运行过程中&#xff0c;内存管理模块通过对内存的申请/释放来管理用户和OS对内存的使用&#xff0c;使内存的利用率和使用效率达到最优&#x…

优思学院|质量工程师工资不高怎么办?

你是否曾经好奇&#xff0c;为什么在职场中&#xff0c;质量工程师的工资普遍不高&#xff1f;这一现象背后的原因&#xff0c;实际上与他们的职业门槛和专业知识密切相关。早期&#xff0c;国内的质量工程师入行门槛较低&#xff0c;许多人即使没有任何专业知识也可以进入这一…

2025 QS 世界大学排名公布,北大清华跻身全球前20

一年一度&#xff0c;2025 QS 世界大学排名公布&#xff01; QS&#xff08;Quacquarelli Symonds&#xff09;是唯一一个同时将就业能力与可持续发展纳入评价体系的排名。 继去年 2024 QS 排名因为“墨尔本超耶鲁&#xff0c;新南悉尼高清华”而荣登微博热搜之后&#xff0c…