【1.4】动态规划-解目标和

一、题目

给你一个整数数组nums和一个整数target 。
向数组中的每个整数前添加'+'或' - ',然后串联起所有整数,可以构造一个表达式:
例 如 , nums=[2,1] , 可 以 在 2 之 前 添 加 '+' , 在 1 之 前 添 加 ' - ' , 然 后串联起来得到表达式"+2-1"。
返回可以通过上述方法构造的、运算结果等于target的不同表达式的数目

二、求解思路

动态规划解决

我们假设在一些数字前添加“+”,这些数字的和是plusSum。剩下的数字前添加“ - ”,这些数字的和是minusSum。我们要求的是
plusSum-minusSum=target ①
的方案数目。
假设数组中所有元素的和是sum。那么我们可以得到
plusSum+minusSum=sum ②
由公式 和公式 我们可以得到
minusSum*2=sum- target;
我们可以看到如果要让上面等式成立, sum- target必须是偶数
也就是说如果 sum- target 不是偶数,无论怎么添加符号 , 表达式的值都不可能是target,直接返回0。
如果sum- target是偶数,我们只需要找出一些数字让他们的和等于minusSum,也就是( sum- target) /2的方案数。
通 过 上 面 的 分 析 , 这 题 就 变 成 了 从 数 组 中 选 择 一 些 元 素 , 让 他 们 的 和 等 于 ( sumtarget) /2的方案数 。这和0-1背包非常像,具体可以看下 背包问题系列之-基础背 包问题 。
我们定义 dp[i][j]表示从数组前i个元素中选取一些数字,让他们的和等于j的方案数 。很明显我们最终只需要返回dp[length][( sum- target) /2]即可。
其中dp[0][0]=1,表示选择0个元素让他们的和等于0,只有一种方案。
遍 历 到 当 前 数 字 num 的 时 候 , 如 果 当 前 数 字 num 大 于 j , 那 么 我 们 是 不 能 选 择 的 , 所 以 dp[i][j]=dp[i -1][j] 。他表示的意思就是前i个元素中不选择第i个元素,而选择前i个元 素中其他的一些数字,让他们的和等于j的方案数。
如果 当 前 数 字 num 小 于 或 等 于 j , 我 们 可 以 选 择 也 可 以 不 选 择 。 如 果 不 选 择 就 是 dp[i][j]=dp[i -1][j],如果选择就是dp[i][j]=dp[i -1][j -num];那么总的方案数就是
dp[i][j]=dp[i -1][j]+dp[i -1][j -num] 。

递推公式如下

if ( j >= num ) { //不选num和选num
    dp[ i ][ j ] = dp[ i - 1 ][ j ]  + dp[ i - 1 ][ j - num ];
} else { //不能选择num
    dp[ i ][ j ] = dp[ i - 1 ][ j ];
}

三、代码实现

通过上面的分析,我们再来看下最终代码

#include <iostream>
#include <vector>

int findTargetSumWays(std::vector<int>& nums, int target) {
    int length = nums.size();
    // 求数组中所有数字的和
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    // 如果所有数字的和小于target,或者sum - target是奇数,
    // 说明无论怎么添加符号,表达式的值都不可能是target
    if (sum < target || ((sum - target) & 1) != 0) {
        return 0;
    }
    // 我们要找到一些元素让他们的和等于capacity的方案数即可。
    int capacity = (sum - target) >> 1;
    // dp[i][j]表示在数组nums的前i个元素中选择一些元素,
    // 使得选择的元素之和等于j的方案数
    std::vector<std::vector<int>> dp(length + 1, std::vector<int>(capacity + 1, 0));
    // 边界条件
    dp[0][0] = 1;
    for (int i = 1; i <= length; i++) {
        for (int j = 0; j <= capacity; j++) {
            // 动态规划公式
            if (j >= nums[i - 1]) { // 不选第i个和选第i个元素
                dp[i][j] = dp[i - 1][j] + dp[i - 1][j - nums[i - 1]];
            }
            else { // 不能选择第i个元素
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
    // 从数组前length个(也就是全部)元素中选择一些元素,让他们的
    // 和等于capacity的方案数。
    return dp[length][capacity];
}



// 这里粘贴之前的 findTargetSumWays 函数定义

int main() {
    std::vector<int> nums = { 1, 1, 1, 1, 1 }; // 示例数组
    int target = 3; // 目标和
    int result = findTargetSumWays(nums, target); // 调用函数计算方案数

    std::cout << "Number of ways to reach target sum: " << result << std::endl;

    return 0;
}

时间复杂度:O(n* capacity),n是数组的长度。
空间复杂度:O(n* capacity),capacity是( sum- target) /2。

        我们看到上面二维数组计算的时候,当前那一行的值只和上一行的有关,所以我们可以改成一维数组,这里要注意嵌套中的第二个for循环要倒叙遍历。因为改成一维数组之后,数组后面的值要依赖前面的(改变之前的),如果从前往后遍历,前面的值被修改了,会导致后面的运行结果错误。如果倒叙,也就是先计算数组后面的值,因为前面的还没有计算,也就是还没有被修改,所以不会导致结果错误。来看下代码

代码优化:

#include <vector>

int findTargetSumWays(std::vector<int>& nums, int target) {
    int length = nums.size();
    // 求数组中所有数字的和
    int sum = 0;
    for (int num : nums) {
        sum += num;
    }
    // 如果所有数字的和小于target,或者sum - target是奇数,
    // 说明无论怎么添加符号,表达式的值都不可能是target
    if (sum < target || ((sum - target) & 1) != 0) {
        return 0;
    }
    // 我们要找到一些元素让他们的和等于capacity的方案数即可。
    int capacity = (sum - target) >> 1;
    std::vector<int> dp(capacity + 1, 0);
    // 边界条件
    dp[0] = 1;
    for (int i = 0; i < length; i++) {
        // 注意,这里要倒序
        for (int j = capacity; j >= nums[i]; j--) {
            // 动态规划公式
            dp[j] += dp[j - nums[i]];
        }
    }
    return dp[capacity];
}

// Main 函数的示例
int main() {
    std::vector<int> nums = {1, 1, 1, 1, 1}; // 示例数组
    int target = 3; // 目标和
    int result = findTargetSumWays(nums, target); // 调用函数计算方案数

    std::cout << "Number of ways to reach target sum: " << result << std::endl;

    return 0;
}

时间复杂度:O(n* capacity)
空间复杂度:O(capacity)

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

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

相关文章

常见WAF拦截页面总结

(1) D盾 (2) 云锁 (3) UPUPW安全防护 (4) 宝塔网站防火墙 (5) 网防G01 (6) 护卫神 (7) 网站安全狗 (8) 智创防火墙 (9) 360主机卫士或360webscan (10) 西数WTS-WAF (11) Naxsi WAF (12) 腾讯云 (13) 腾讯宙斯盾 (14) 百度云 图片 (15) 华为云 (16) 网宿云 (17) 创宇盾 图片 (…

(自用)gtest单元测试

gtest是Google的一套用于编写C测试的框架&#xff0c;可以运行在很多平台上&#xff08;包括Linux、Mac OS X、Windows、Cygwin等等&#xff09;。基于xUnit架构。支持很多好用的特性&#xff0c;包括自动识别测试、丰富的断言、断言自定义、死亡测试、非终止的失败、生成XML报…

《Programming from the Ground Up》阅读笔记:p19-p48

《Programming from the Ground Up》学习第2天&#xff0c;p19-p48总结&#xff0c;总计30页。 一、技术总结 1.object file p20, An object file is code that is in the machine’s language, but has not been completely put together。 之前在很多地方都看到object fi…

RAG的学习与实践——LangChain和LlamaIndex学习笔记

RAG RAG(Retrieval Augmented Generation)系统&#xff0c;代表“检索增强生成”。RAG由五个关键步骤组成&#xff1a; 加载&#xff1a;这是指将数据从其所在位置&#xff08;无论是文本文件、PDF、其他网站、数据库还是 API&#xff09;获取到您的管道中。LlamaHub提供数百…

【南京蓝领新材料】水力颗粒分离器工作原理

水力颗粒分离器工作原理 在装置内部设有一个具有一定空间的滤网&#xff0c;雨水从进水管流入&#xff0c;先进入滤网过滤&#xff0c;雨水中的悬浮物和漂浮物将被拦截在此滤网内。 在装置底部有三个腔室&#xff0c;当雨水中小的颗粒物流到每个腔室挡墙前时&#xff0c;颗粒物…

react学习——25redux实现求和案例(完整版)

1、目录结构 2、count/index.js import React, {Component} from "react"; //引入store,用于获取数据 import store from ../../redux/store //引入actionCreator 专门创建action对象 import {createDecrementAction,createIncrementAction} from ../../redux/coun…

机器学习与深度学习:区别与联系(含工作站硬件推荐)

一、机器学习与深度学习区别 机器学习&#xff08;ML&#xff1a;Machine Learning&#xff09;与深度学习&#xff08;DL&#xff1a;Deep Learning&#xff09;是人工智能&#xff08;AI&#xff09;领域内两个重要但不同的技术。它们在定义、数据依赖性以及硬件依赖性等方面…

数字人+展厅互动体验方案:多元化互动方式,拓宽文化文娱新体验

数字化创新已成为推动展厅可持续发展&#xff0c;创造全新消费体验&#xff0c;满足游客多元化需求的关键力量。 “数字人数字互动展厅”可以适应年轻一代的文化传播与多媒体互动新体验趋势&#xff0c;打造新生代潮玩聚集地&#xff0c;促进文化创意传播与互动体验场景创新&a…

storybook中剔除chakra-ui的影响,或者剔除其他ui包的影响

介绍 经过一系列初始化完成后&#xff0c;storybook项目启动出来发现多余了一个ui框架的内容。如下图 因为项目中仅仅使用chakraUI的一些功能&#xff0c;并没有使用整体组件功能&#xff0c;所以说完全没必要把它留着这里。经过排查可以使用storybook中的refs功能剔除掉不需要…

【数智化案例展】厦门市信息中心——爱数助力厦门政务云构建两地三中心多级数据灾备体系...

‍ 爱数案例 本项目案例由爱数投递并参与数据猿与上海大数据联盟联合推出的《2024中国数智化转型升级创新服务企业》榜单/奖项”评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 厦门市信息中心是厦门市电子政务专门机构&#xff0c;加挂厦门市电子政务中心、厦门市大数…

windows驱动开发基础-环境篇

前言 Windows上无论是用户模式下还是内核模式下&#xff0c;有关驱动的开发都有可能影响系统稳定性&#xff0c;所以我们首先要准备一个专用的测试环境&#xff0c;可以使用VM等虚拟机方便环境修复和还原 测试模式 开启测试模式&#xff1a;cmd 命令 bcdedit /set testsign…

视频共享交换平台LntonCVS视频监控平台智慧加油站安全管理方案

加油站作为危化品行业的一部分&#xff0c;日常的加油和卸油作业安全至关重要。目前国内加油站的管理主要依赖于人为管控、监控摄像头和人工巡检&#xff0c;这些方法存在效率低下和反应滞后的问题。为了有效应对安全风险&#xff0c;急需引入人工智能、物联网和大数据技术&…

视频版权音乐处理☞AI分离人声、音效、背景音乐的需求和进展-2024

随着互联网的普及和短视频的兴起&#xff0c;视频内容的全球各大平台分发越来越普遍。然而&#xff0c;不同国家和地区的音乐版权、不同社媒平台拥有的版权和处理政策都存在差异&#xff0c;因此同一个视频在多渠道分发的时候就会产生版权侵权风险。如何既能满足全球多渠道、多…

C++Windows环境搭建(CLion)

文章目录 CLion下载安装CLion下载CLion安装新建项目新建一个文件基础设置字体设置clion中单工程多main函数设置 参考 CLion下载安装 CLion下载 打开网址&#xff1a;https://www.jetbrains.com/clion/download/ 点击Download进行下载。 CLion安装 双击下载好的安装包&…

M3U8 视频是一种什么格式,M3U8 视频怎么转成 MP4

M3U8 文件格式在流媒体服务中非常常见&#xff0c;尤其是与 HTTP Live Streaming (HLS) 协议结合使用时。HLS 是苹果公司开发的一种流媒体传输协议&#xff0c;旨在为 iOS 设备和 Safari 浏览器提供高质量的流媒体播放体验。M3U8 文件在这种情况下充当了索引角色&#xff0c;指…

如何用Vue3和Plotly.js绘制交互式瀑布图

本文由ScriptEcho平台提供技术支持 项目地址&#xff1a;传送门 使用 Plotly.js 在 Vue 中创建瀑布图 应用场景 瀑布图广泛用于可视化财务报表和展示增量变化&#xff0c;例如利润表、现金流量表和收入分析。它们通过将正值和负值堆叠在垂直轴上&#xff0c;清晰地展示每个…

Win10屏幕录制,这3种方法分享给你

数字化时代里&#xff0c;电脑的屏幕录制功能已经不再是简单的工具&#xff0c;而是成为我们表达、学习和交流的重要媒介。Win10系统依然是大部分人使用的电脑系统&#xff0c;那么关于Win10屏幕录制&#xff0c;有哪些好用高效的录制软件&#xff0c;能够帮助我们更加深入地捕…

Qt:11.输入类控件(QLineEdit-单行文本输入控件、QTextEdit-多行文本输入控件、QComboBox-下拉列表的控件)

一、QLineEdit-单行文本输入控件&#xff1a; 1.1QLineEdit介绍&#xff1a; QLineEdit 是 Qt 库中的一个单行文本输入控件&#xff0c;不能换行。允许用户输入和编辑单行文本。 1.2属性介绍&#xff1a; inputMask 设置输入掩码&#xff0c;以限定输入格式。setInputMask(con…

Java内存区域与内存溢出异常(补充)

2.2.5 方法区 方法区(Method Area)与Java堆一样&#xff0c;是各个线程共享的内存区域&#xff0c;它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分&#xff0c;但是它却有一…

【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——红黑树

目录 1 -> 红黑树 1.1 -> 红黑树的概念 1.2 -> 红黑树的性质 1.3 -> 红黑树节点的定义 1.4 -> 红黑树的结构 1.5 -> 红黑树的插入操作 1.6 -> 红黑树的验证 1.8 -> 红黑树与AVL树的比较 2 -> 红黑树模拟实现STL中的map与set 2.1 -> 红…