动态规划:01背包问题例题(leetcode+cpp实现)

news2024/11/18 23:22:15

文章目录

  • 分割等和子集
  • 最后一块石头的重量

前情回顾:
动态规划(4):01背包问题详解

分割等和子集

力扣传送门:
https://leetcode.cn/problems/partition-equal-subset-sum/

题目描述:

给你一个整数数组,将这个数组里的元素分成两部分,每一部分的元素之和相等,能够被分割返回true,否则,返回零

这道题是动态规划01背包问题的一个例题,我们通过这道题可以训练一下01背包问题的变形及解法。

当然,这道题也可以采用暴力解法,即通过回溯枚举出所有子集,然后分别求出它们的和,最后再比较是否存在两个子集是相等的,当然这样做的时间复杂度很高,我们使用动态规划可以简化这道题的时间复杂度。

动态规划五步走:

  1. 确定dp数组以及下标的含义
  2. 递推公式的确定
  3. dp数组的初始化
  4. dp数组的遍历过程
  5. 图例推导验证dp过程

首先分析:

分割数组,即意味着数组的和可以被分成两份:每份的和都是相等的,这表示可以分割这个数组:
例如:[1,5,11,5] 的总和为 22
22可以被分为 11 和 11 ,每一份的和都是11,因此只需求证每一份的和是否等于数组中和的一半就好了。

另外,如果数组的和的一半不能被整除,如21,则不能被分成两个部分,因此只需判断一下能否被整除即可判断是否返回false。


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

我们创建dp[i][j]二维数组,其中 i 表示选择的物品编号i,j 表示的是背包的容量j
在这道题中,我们可以把数组的某一个元素nums[i]看作物品i;然后我们把数组的元素之和看作整个背包的最大容量,那么此时 j 就表示了在容量 j 下的背包的子容量;并且我们把物品的价值也看作是元素值

dp[i][j]:表示了容量为j的背包,把一个元素i放入了这个背包,这个背包的最大价值(最大元素和)是多少。

在本题中,我们的背包容量为 数组总和的一半(目的是为了验证dp数组的最后一个是否等于整数数组总和的一半,相等则返回true)。

 int sum=accumulate(nums.begin(), nums.end(), 0);
 if (sum%2) return false;
 int weight=sum/2;
 //初始化dp数组为和的一半
 vector<vector<int>> dp(n,vector<int>(weight+1));

  1. 确定递推公式

在上一节中,我们详解解析了01背包问题的递推公式,在这里我们不过多解释,这道题既然可以转换为01背包的问题,那么对于此递推公式也是同样使用的。

  • 把元素i 放入背包中:dp[i-1][j-nums[i]]+nums[i]

  • 不把元素i 放入背包中:dp[i][j]=dp[i-1][j]

多罗嗦两句:

  • . dp[i][j]=dp[i-1][j]:表示的是当前背包的容量不足以再容纳这个物品i(元素),因此无法存储此物品i,此时的价值还等于上一次的物品的价值。
  • . dp[i-1][j-nums[i]]+nums[i]:表示的是:此时背包容量足够容纳这个物品,把这个物品 i 放入容量为 j - nums[i] 背包中,加上此物品的价值 nums[i](由于在本题中是整数数组,我们假设元素大小就是物品重量,同时也是物品价值) ,即表示了我们背包的当前价值。

  1. dp数组如何初始化
  • 第一行初始化:
for (int j=nums[0];j<=weight;j++)
{
    dp[0][j]=nums[0];
}

当物品编号为0时,此时表示的就是选取了数组中第一个元素,我们的背包容量 j(j>=1),总能容纳第一个元素,所以第一行初始化为第一个元素的大小

  • 第一列初始化:
    当背包的容量为0时,任何物品都不能容纳,直接第一列初始化为0,表示不能放任何编号的物品。
    由于数组的创建本身就初始化为0,所以可以不必写代码。

在这里插入图片描述


  1. dp数组的遍历过程

两种方式,都可以,具体请看我的上一篇博客。

首先遍历物品(元素),然后遍历背包的容量

或者遍历背包的容量,然后遍历物品(元素)。


  1. 图模拟dp过程:

如图:行 i,列 j,分别表示了背包容量为j时,放入物品i时的价值dp[i][j],此时位于整个数组的最后一个元素就是我们的最大价值。
在这里插入图片描述
所以,我们的dp数组的最后一个元素的值等于我们的数组和的一半,因此证明了这个数组是可以被分割成等和的子集的,返回true。


代码示例:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size();
        int m=99;
        int sum=accumulate(nums.begin(), nums.end(), 0);
        if (sum%2) return false;
        //以和的一半当作背包的总容量
        int weight=sum/2;
        vector<vector<int>> dp(n,vector<int>(weight+1));
        for (int j=nums[0];j<=weight;j++)
        {
            dp[0][j]=nums[0];
        }
        //先遍历物品,再遍历背包
        for (int i=1;i<n;i++)
        {
            for (int j=0;j<=weight;j++)
            {
                if (j<nums[i]) dp[i][j]=dp[i-1][j];
                else dp[i][j]=max(dp[i-1][j],dp[i-1][j-nums[i]]+nums[i]);
            }
        }
        //判断最后一个元素是不是和的一半
        return dp[n-1].back()==weight;
    }
};

滚动数组优化,动态规划:

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size();
        int sum=accumulate(nums.begin(),nums.end(),0);
        if (sum%2) return false;
        int target=sum/2;
        vector<int> dp(target+1);
        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]);
            }
        }
        return dp.back()==target;
    }
};

最后一块石头的重量

力扣传送门:
https://leetcode.cn/problems/last-stone-weight-ii/

本题不再过多描述,详细请移步leetcode。

直接解析:

既然要每次选出两块石头,然后依次两块两块进行操作,我们可以大胆的把这个石头数组分成两堆。

我们的dp数组描述其中的一堆。

和上一题完全一致:

  1. 确定dp数组以及其下标的含义

dp[i][j]:表示把石头编号为i的放入背包容量为j的背包中,当前背包的最大价值(石头的总重量)。

  1. 递推公式的确定
  • 不放石头:dp[i][j]=dp[i-1][j]
  • 放石头: dp[i][j]=dp[i-1][j-stone[i]]+stone[i]
  1. dp数组的初始化

我们的背包容量j (j>=1)一定可以存储第一块石头,第一行初始化为第一块石头的重量
背包的容量为0时,无法放任何一块石头,第一列的初始化。

  1. dp的遍历
  2. 图例推导dp数组

我们dp数组最后存储的是这一堆的石头的总重量,sum - 这一堆 =另一堆,

因此

另一堆 - 这一堆 = 最后的结果

普通的动态规划:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=accumulate(stones.begin(),stones.end(),0);
        int target=sum/2;
        int n=stones.size();
        vector<vector<int>> dp(n,vector<int>(target+1));
        for (int j=stones[0];j<=target;j++)
        {
            dp[0][j]=stones[0];
        }
        for (int i=1;i<n;i++)
        {
            for (int j=0;j<=target;j++)
            {
                if (j<stones[i]) dp[i][j]=dp[i-1][j];
                else dp[i][j]=max(dp[i-1][j],dp[i-1][j-stones[i]]+stones[i]);
            }
        }
        //sum - dp[n-1].back()
        return sum-dp[n-1].back()-dp[n-1].back();
    }
};

滚动数组优化,动态数组:

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=accumulate(stones.begin(),stones.end(),0);
        int target=sum/2;
        int n=stones.size();
        vector<int> dp(target+1);
        //背包重量为0时,啥也不能放
        dp[0]=0;
        for (int i=0;i<n;i++)
        {
            for (int j=target;j>=stones[i];j--)
            {
                dp[j]=max(dp[j],dp[j-stones[i]]+stones[i]);
            }
        }
        return sum-dp[target]-dp[target];
    }
};

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

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

相关文章

论文速读Backbone系列一:点云Transformer结合、PointNet++改进、点云卷积核设计

如有错误&#xff0c;恳请指出。 对一些经典论文进行快速思路整理&#xff0c;以下内容主要关注的是3d点云的backbone设计&#xff0c;包括transformer的应用&#xff0c;卷积核的设计&#xff0c;PointNet网络的改进。 文章目录一、Transformer改进1. 《PCT: Point Cloud Tran…

Wireshark高级特征

1&#xff0c;端点和网络会话 想要让网络通信正常进行&#xff0c;你必须至少拥有两台设备进行数据流的交互。端点&#xff08;endpoint&#xff09;就是指网络上能够发送或接收数据的一台设备。两个端点之间的通信被称之为会话&#xff08;conversation&#xff09;。Wiresha…

Python中如何选择Web开发框架?

Python开发中Web框架可谓是百花齐放,各式各样的web框架层出不穷,那么对于需要进行Python开发的我们来说,如何选择web框架也就变成了一门学问了。本篇文章主要是介绍目前一些比较有特点受欢迎的Web框架,我们可以根据各个Web框架的特性进行选择应用。 Django Django是市面上…

利用Astar算法实现飞行轨迹的三维路径规划(基于Matlab代码实现)

目录 1 概述 1.1研究背景 2 运行结果 3 Matlab代码实现 4 参考文献 1 概述 随着自动化技术的发展,现代航空技术水平有了前所未有的提高,促进了无人机在军事、民用领域的广泛应用。航迹规划技术作为无人机任务规划的关键技术,一直都是无人机领域的一大研究热点。无人机航迹规划是…

Synopsys Sentaurus TCAD系列教程之-- Svisual《一》看图工具

Svisual Svisual作为独立的模块&#xff0c;可以实现Inspect对于节点.plt曲线的查看&#xff0c;也可以实现TecplotSV的.tdr看图功能&#xff0c;还可以进行自动化操作。 当作看图工具使用 - 当作一维曲线看图工具&#xff08;.plt,Inspect) 1.1 打开&#xff1a;Sdevice模块…

[附源码]Python计算机毕业设计SSM基于自组网的空地一体化信息系统(程序+LW)

[附源码]Python计算机毕业设计SSM基于自组网的空地一体化信息系统&#xff08;程序LW) 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff0…

F12抓包简单案例

F12抓包原理&#xff1a;在电脑访问互联网之前&#xff0c;进行包录制 1.谷歌开发者工具&#xff08;F12&#xff09; 元素&#xff08;Elements&#xff09;&#xff1a;页面元素查看分析&#xff0c;web自动化 控制台&#xff08;Console&#xff09;:查看日志 来源&#xf…

c语言---指针进阶(2)--玩转指针

今天内容不多&#xff0c;但都是精华。 1.数组参数和指针参数 2.函数指针 2.1笔试题 3.函数指针数组 1.数组参数和指针参数 例1&#xff1a;一维数组传参 void test(int arr[]) {} void test(int arr[10]) {} void test(int *arr) {}void test2(int *arr2[20]) {} void …

kotlin之声明变量的方式

让我们来看一下java声明变量的方式 Java声明变量方式 类型 变量名 等于 初始化值 String str "999999999"; kotlin声明变量方式 <关键字> <变量名称> : <类型> <初始化值> var sr : String "s…

[附源码]Python计算机毕业设计SSM计算机软考系统的设计与实现(程序+LW)

[附源码]Python计算机毕业设计SSM计算机软考系统的设计与实现&#xff08;程序LW) 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。…

nginx安装及使用(详细版)

一、负载均衡介绍 负载均衡分为硬件负载均衡和软件负载均衡。 硬件负载均衡解决方案是最直接服务器和外部网络间安装负载均衡设备&#xff0c;这种设备我们通常称之为负载均衡器产品有F5、NetScaler等。 软件负载均衡解决方案是指一台或多台服务器响应的操作系统上安装一个或多…

【旋转目标检测】旋转标注工具roLabelImg使用教程

旋转目标检测系列文章:第一篇,学会使用roLabelImg标注工具 本次实验在Windows 10系统下进行, python版本3.6.10(2年前安装的python版本,有点老了) 下载标注工具项目代码: https://github.com/cgvict/roLabelImg 下载代码压缩包到桌面: 解压: 打开Anaconda Prompt终…

动态拉取接口数据

通过工具类动态拉取接口数据 写在前言&#xff1a; 使用工具类拉取接口中的数据&#xff0c;存放在自己的数据库&#xff0c;进行展示。 自己去看工具类中的注释&#xff0c;按照自己的需求去修改。 一、工具类 1、整理需求 想通过工具类把接口数据存放在数据库&#xff…

Feign远程调用

Feign是一个声明式的http客户端&#xff0c;官方地址&#xff1a;https://github.com/OpenFeign/feign Fegin的使用步骤如下&#xff1a; 1&#xff09;引入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-clou…

Win11开始菜单里面的推荐项目怎么全部关闭教学

Win11开始菜单里面的推荐项目怎么全部关闭教学分享。在Win11系统的开始菜单中&#xff0c;有一些推荐的功能程序。这些程序用户平时并没有使用到&#xff0c;所以想要去将它们进行关闭。那么这些程序如何彻底的关闭显示呢&#xff1f;一起来看看关闭的方法吧。 操作方法&#x…

[附源码]计算机毕业设计咖啡销售平台Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[设计] Doris血缘解析流程

一、背景 1.1 元数据概述 元数据是凌久中台重要功能模块&#xff0c;是数据治理的重要一环&#xff0c;元数据治理是一切数据治理的基础&#xff0c;主要分为元数据管理和表血缘管理&#xff1b; 元数据管理主要用来做数据地图、数据资产等&#xff1b; 血缘治理主要用来追查…

爬虫-(5)

内容概览 scrapy架构介绍scrapy解析数据settings相关配置持久化方案爬取全站文章爬虫中间件与下载中间件 scrapy架构介绍 # 引擎(ENGINE) 引擎负责控制系统所有组件之间的数据流&#xff0c;并在某些动作发生时触发事件。有关详细信息&#xff0c;请参见上面的数据流部分。# …

SLURM批处理调度系统介绍

SLURM (Simple Linux Utility for Resouce Management) 非常成熟的开源社区集群批处理调度系统。 介绍 2008年左右起源于LLNL实验室, 最新版本20.11开源软件(约50万行c源码)&#xff0c;开发和发布一直活跃用户群: Top10里有5家使用(天河HPC是其一)商业公司SchedMD提供顾问咨…

经典算法之LRU算法

一、理论 LRU算法算是个常见的算法&#xff0c;很有必要去了解它&#xff0c;现在我们就来看看什么是 LRU LRU 的全称是 Least Recently Used&#xff08;最近最少使用&#xff09;&#xff0c;就如它的含义一样&#xff0c;最近最少使用的。在实际的场景中大多会把它当作一种…