动态规划解决目标和问题

代码随想录链接:代码随想录

思路:

可以将数组分为两部分,其中一部分记作left,其中数字的符号全为+,而另外一部分记作right,其中数字的符号全为-。这里全为-的意思不是真正的符号为-,而表示这一堆数字在计算时取值为负

因此有如下的关系:

left + right = sum,其中sum为数组和

left - right = target,其中target为目标值

可以得出left = (sum + target) / 2

因此原问题转换为在集合nums中找出和为left的组合共有多少种

也就是用nums装满容量为x的背包,有几种方法。这里的x,就是bagSize,也就是我们后面要求的背包容量

方法一:使用二维dp数组求解:

(1).确定dp数组的形式以及对应的含义:

dp[i][j]:使用原始数组下标为[0,i]的元素能够凑满j(包括j)这么大容量的包,有dp[i][j]种方法

其中dp数组的维度大小为N*(left+1),其中N为原始数组中数据的个数,而left =  (sum + target) / 2

(2).确定递推公式:

对于dp数组中的元素dp[i][j]来说,有两种推导方式:

第一种是不放元素i,此时背包容量为j,里面不放物品i,装满有dp[i - 1][j]中方法

第二种是放元素i,此时需要先空出物品i的容量,因此背包容量为(j - 物品i容量),放满背包有dp[i - 1][j - 物品i容量]种方法

需要注意的是,当j - nums[i]小于零时,表示此时背包无法放入物品i,此时装满背包的方法值 等于不放物品i的装满背包的方法,即:dp[i][j] = dp[i - 1][j]

因此有如下的推导公式:

if (nums[i] > j) dp[i][j] = dp[i - 1][j]; 
else dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];

 (3).dp数组的初始化:

由于递推公式需要左上方的元素,因此需要初始化第一行和第一列

dp[0][0]表示装满背包容量为0 的方法数量是1,即 放0件物品,因此dp[0][0] = 1

初始化第一行dp[0][j]:

dp[0][j]表示只放物品0, 把容量为j的背包填满有几种方法

因此:只有背包容量为物品0的容量的时候,方法为1,正好装满。其他情况下,要不是装不满,要不是装不下。所以初始化dp[0][nums[0]] = 1 ,其他均为0

初始化第一列dp[i][0]:

dp[i][0]表示背包容量为0, 放物品0到物品i装满有几种方法。此时由于背包的容量为0,因此只有一种方法,因此第一列全为1

但是如果原始数组中有0元素,此时dp[i][0]应该为2的n次方,其中n为元素[0,i]中0的个数

(4).确定遍历顺序:

遍历顺序一定是从上到下,从左到右

Java代码:

class Solution {
    public int findTargetSumWays(int[] nums, int target) {

        // 01背包应用之“有多少种不同的填满背包最大容量的方法“
        // 易于理解的二维数组解法及详细注释

        int sum = 0;
        for(int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }

        // 注意nums[i] >= 0的题目条件,意味着sum也是所有nums[i]的绝对值之和
        // 这里保证了sum + target一定是大于等于零的,也就是left大于等于零(毕竟我们定义left大于right)
        if(sum < Math.abs(target)){
            return 0;
        }

        // 利用二元一次方程组将left用target和sum表示出来(替换掉right组合),详见代码随想录对此题的分析
        // 如果所求的left数组和为小数,则作为整数数组的nums里的任何元素自然是没有办法凑出这个小数的
        if((sum + target) % 2 != 0) {
            return 0;
        }

        int left = (sum + target) / 2;
        
        // dp[i][j]:遍历到数组第i个数时, left为j时的能装满背包的方法总数
        int[][] dp = new int[nums.length][left + 1];

        // 初始化最上行(dp[0][j]),当nums[0] == j时(注意nums[0]和j都一定是大于等于零的,因此不需要判断等于-j时的情况),有唯一一种取法可取到j,dp[0][j]此时等于1
        // 其他情况dp[0][j] = 0
        // java整数数组默认初始值为0
        if (nums[0] <= left) {
            dp[0][nums[0]] = 1;
        }

        // 初始化最左列(dp[i][0])
        // 当从nums数组的索引0到i的部分有n个0时(n > 0),每个0可以取+/-,因此有2的n次方中可以取到j = 0的方案
        // n = 0说明当前遍历到的数组部分没有0全为正数,因此只有一种方案可以取到j = 0(就是所有数都不取)
        int numZeros = 0;
        for(int i = 0; i < nums.length; i++) {
            if(nums[i] == 0) {
                numZeros++;
            }
            dp[i][0] = (int) Math.pow(2, numZeros);

        }

        // 递推公式分析:
        // 当nums[i] > j时,这时候nums[i]一定不能取,所以是dp[i - 1][j]种方案数
        // nums[i] <= j时,num[i]可取可不取,因此方案数是dp[i - 1][j] + dp[i - 1][j - nums[i]]
        // 由递推公式可知,先遍历i或j都可
        for(int i = 1; i < nums.length; i++) {
            for(int j = 1; j <= left; j++) {
                if(nums[i] > j) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];
                }
            }
        }


        return dp[nums.length - 1][left];
        
    }
}

方法二:使用一维dp数组

重复利用每一行,就是将二维数组压缩成一行

(1).确定dp数组的含义:

dp[i][j]去掉行的维度,即dp[j]表示填满j(包括j)这么大容积的包,有dp[j]种方法

(2).确定递推公式:

二维DP数组递推公式:dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i]];

去掉维度i之后,递推公式:dp[j] = dp[j] + dp[j - nums[i]] ,即:dp[j] += dp[j - nums[i]]

(3).dp数组的初始化:

dp[0] = 1,表示填满容量为0的包共有一种方法

(4).确定遍历顺序:

遍历物品放在外循环,遍历背包在内循环,且内循环倒序(为了保证物品只使用一次)

class Solution {
    public int findTargetSumWays(int[] nums, int target) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) sum += nums[i];

        //如果target的绝对值大于sum,那么是没有方案的
        if (Math.abs(target) > sum) return 0;
        //如果(target+sum)除以2的余数不为0,也是没有方案的
        if ((target + sum) % 2 == 1) return 0;

        int bagSize = (target + sum) / 2;
        int[] dp = new int[bagSize + 1];
        dp[0] = 1;

        for (int i = 0; i < nums.length; i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }

        return dp[bagSize];
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

unity开发之shader 管道介质流动特效

效果 shader graph 如果出现下面的效果&#xff0c;那是因为你模型的问题&#xff0c;建模做贴图的时候没有设置好UV映射&#xff0c;只需重新设置下映射即可

【JavaWeb】2. 通用基础代码

以下内容来源&#xff1a;编程导航。 无论在任何后端项目中&#xff0c;都可以复用的代码。 1、自定义异常 自定义错误码&#xff0c;对错误进行收敛&#xff0c;便于前端统一处理。 &#x1f4a1; 这里有 2 个小技巧&#xff1a; 自定义错误码时&#xff0c;建议跟主流的错…

Excel 技巧04 - 如何计算两个时间之差 (★)

本文讲了如何通过Excel计算两个时间的时间差。 1&#xff0c;计算两个时间的时间差 比如 5&#xff1a;50 ~ 19&#xff1a;40 a&#xff09;&#xff0c;用公式 相减 这样默认算出来的是机械的时间加减&#xff0c;即它们之间相差了 13小时50分钟 b&#xff09;&#xff0c;…

win下搭建elk并集成springboot

一、ELK 是什么&#xff1f; ELK 实际上是三个工具的集合&#xff0c;Elasticsearch Logstash Kibana&#xff0c;这三个工具组合形成了一套实用、易用的监控架构&#xff0c;很多公司利用它来搭建可视化的海量日志分析平台。 ElasticSearch ElasticSearch 是一个基于 Lucen…

JUC--线程池

线程池 七、线程池7.1线程池的概述7.2线程池的构建与参数ThreadPoolExecutor 的构造方法核心参数线程池的工作原理 Executors构造方法newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutornewScheduledThreadPool(int corePoolSize) 为什么不推荐使用内置线程池&…

Java到底是值传递还是引用传递????

在搞懂这个问题之前, 我们要首先了解什么是值传递, 什么是引用传递? 值传递: 传递的是数据的副本&#xff0c;修改副本不会影响原始数据。引用传递: 传递的是数据的引用&#xff08;地址&#xff09;&#xff0c;修改引用会直接影响原始数据. 也就是说&#xff0c;值传递和引…

屏幕显示技术再突破!海信RGB- Mini LED,让色彩“活”起来

文 | 智能相对论 作者 | 佘凯文 在今天&#xff0c;屏幕显示技术的日新月异&#xff0c;让每次技术革新都引领行业迈向新的高度。 从黑白到彩色&#xff0c;从标清到高清&#xff0c;再到超高清&#xff0c;回顾曾经彩电显示的技术升级&#xff0c;不仅都极大地提升了观众的…

豆包ai 生成动态tree 增、删、改以及上移下移 html+jquery

[豆包ai 生成动态tree 增、删、改以及上移下移 htmljquery) 人工Ai 编程 推荐一Kimi https://kimi.moonshot.cn/ 推荐二 豆包https://www.doubao.com/ 实现效果图 html 代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF…

基于SMT32U575RIT单片机-中断练习

任务 查看手册对所有的拓展板上和相对应的底板的引脚对应的端口找到以下结论 通过STM32MX软件对各个引脚进行相应的配置 1.第一种切换模式电脑发送 #include "main.h" #include "icache.h" #include "usart.h" #include "gpio.h"/*…

HNU人工智能期末复习知识点整理

考纲 选择题 ( 30 分 ) (30分) (30分)&#xff1a; 15 15 15个单选 选择题范围为 PPT 内容&#xff0b;课本内容 计算、简答、推理题 ( 70 分 ) (70分) (70分)&#xff1a; 4 4 4个大题&#xff0c;每个大题 2 ∼ 3 2 \sim 3 2∼3小问 4 4 4个大题分别为&#xff1a;机器学习、…

设计DCDC的 Layout的秘诀

很多DCDC芯片的手册都有对应的PCB Layout设计要求&#xff0c;有些还会提供一些Layout示意图&#xff0c;都是大同小异的。 比如我随便列几点buck的设计要点&#xff1a; 1、输入电容器和二极管在与IC相同的面&#xff0c;尽可能在IC最近处。 2、电感靠近芯片的SW&#xff0c;输…

自动驾驶控制与规划——Project 6: A* Route Planning

目录 零、任务介绍一、算法原理1.1 A* Algorithm1.2 启发函数 二、代码实现三、结果分析四、效果展示4.1 Dijkstra距离4.2 Manhatten距离4.3 欧几里德距离4.4 对角距离 五、后记 零、任务介绍 carla-ros-bridge/src/ros-bridge/carla_shenlan_projects/carla_shenlan_a_star_p…

单纯形法的学习笔记

文章目录 A. 单纯形法概述1. 优化模型示例 B. 理论基础C. 算法思想D. 实现算法1. 线性规划的标准型2. 顶点解的理解及表示2.1 在标准型中变量取值为零的意义2.2 顶点解的表示 3. 最优性判断4. 解的更新5. 完成迭代过程 E. 单纯形法的基本概念与本文对照F. 文档源码 前言&#x…

ArmSoM RK3588/RK3576核心板,开发板网络设置

ArmSoM系列产品都搭配了以太网口或WIFI模块&#xff0c;PCIE转以太网模块、 USB转以太网模块等&#xff0c;这样我们的网络需求就不止是上网这么简单了&#xff0c;可以衍生出多种不同的玩法。 1. 网络连接​ 连接互联网或者组成局域网都需要满足一个前提–设备需要获取到ip&a…

[Linux]线程概念与控制

目录 一、线程概念 1.什么是线程 2.线程的轻量化 3.LWP字段 4.局部性原理 5.线程的优缺点 6.进程VS线程 二、线程的控制 1.线程创建 2.获取线程id 3.线程退出与等待 4.创建轻量级进程 三、线程的管理 1.pthread库管理线程 2.线程局部存储 四、C线程库 1.构造函…

cmake--库链接--RPATH--RUNPATH

RPATH--RUNPATH RPATH 是一种嵌入到二进制文件(可执行文件/库文件)中的路径信息&#xff0c;也就是存在于可执行文件或者库文件中的&#xff0c; 用RPATH(旧)或者RUNPATH(新)参数记录的路径信息&#xff0c; 指示动态链接器在运行时查找共享库的位置。 查看二进制文件的RPATH或…

Chapter 4.4:Adding shortcut connections

4 Implementing a GPT model from Scratch To Generate Text 4.4 Adding shortcut connections 接下来&#xff0c;让我们讨论 shortcut connections&#xff08;快捷连接&#xff09;背后的概念&#xff0c;也称为 skip connections&#xff08;跳跃连接&#xff09;或 resid…

Web渗透测试之XSS跨站脚本 原理 出现的原因 出现的位置 测试的方法 危害 防御手段 面试题 一篇文章给你说的明明白白

目录 XSS介绍的原理和说明 Cross Site Scripting 钓鱼 XSS攻击原理 XSS漏洞出现的原因&#xff1a; XSS产生的原因分析 XSS出现位置&#xff1a; XSS测试方法 XSS的危害 防御手段&#xff1a; 其它防御 面试题: 备注&#xff1a; XSS介绍的原理和说明 嵌入在客户…

热门数据手套对比,应用方向有何不同?

AI与人形机器人是目前市场中大热的两个新行业。在人形机器人或拟人仿真机器人制造与开发中动作捕捉技术的融入是必不可少的&#xff0c;通过将动捕数据与先进的AI大数据训练技术相结合&#xff0c;不仅能够省去枯燥乏味的动作编程过程大幅减少训练时间&#xff0c;还可以使训练…

dbt Semantic Layer 详细教程-1 :总体概述

dbt 语义模型提供语言描述方式快速定义业务指标。本文介绍语义模型作用和意义&#xff0c;以及语义模型的组成部分&#xff0c;后面会继续介绍如何定义语义模型&#xff0c;基于语义模型定义指标&#xff0c;如何通过MetricFlow&#xff08;语义层框架&#xff09;能够构建用于…