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

news2024/9/29 11:34:53

一、题目

给你一个整数数组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)

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

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

相关文章

[leetcode] car-pooling 拼车

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:bool carPooling(vector<vector<int>>& trips, int capacity) {int to_max 0;for (const auto& trip: trips) {to_max max(to_max, trip[2]);}vector<int> diff(to_max 1);for…

智慧城市大数据运营中心 IOC:Web GIS 地图应用助力智能决策

利用图扑 HT for Web GIS 技术&#xff0c;智慧城市大数据运营中心 (IOC) 实现动态可视化展示&#xff0c;整合多源数据&#xff0c;提高城市管理和资源分配效率&#xff0c;支持智能决策与实时监控。

自定义波形图View,LayoutInflater动态加载控件保存为本地图片

效果图&#xff1a; 页面布局&#xff1a; <?xml version"1.0" encoding"utf-8"?><LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:la…

Web3学习路线图,从入门到精通

前面我们聊了Web3的知识图谱&#xff0c;内容是相当的翔实&#xff0c;要从哪里入手可以快速的入门Web3&#xff0c;本篇就带你看看Web3的学习路线图&#xff0c;一步一步深入学习Web3。 这张图展示了Web3学习路线图&#xff0c;涵盖了区块链基础知识、开发方向、应用开发等内…

Canvas:掌握图像变换合成与裁剪状态像素操作

想象一下&#xff0c;用几行代码就能创造出如此逼真的图像和动画&#xff0c;仿佛将艺术与科技完美融合&#xff0c;前端开发的Canvas技术正是这个数字化时代中最具魔力的一环&#xff0c;它不仅仅是网页的一部分&#xff0c;更是一个无限创意的画布&#xff0c;一个让你的想象…

【深度学习(42)】通过vscode使用anaconda的python环境

按ctrlshiftp&#xff0c;选择Python:Select Interpreter 选择anaconda下的python虚拟环境

【spark】Exception in thread “main“ ExitCodeException exitCode=-1073741701

在window上运行spark程序写到本地文件的时候报错。 val rdd sc.sparkContext.parallelize(list)val arr rdd.collect()arr.foreach(println)rdd.saveAsTextFile("test1")sc.close()错误信息: zhangsan lisi wangwu Exception in thread "main" ExitCode…

连续6年夺冠 6项细分领域第一,中电金信持续领跑中国银行业IT解决方案市场

7月9日&#xff0c;工信部赛迪顾问发布《2023年度中国银行业IT解决方案市场分析报告》&#xff08;简称《报告》&#xff09;。中电金信以7.38%的市场份额再度蝉联2023中国银行业IT解决方案市场份额第一&#xff0c;以显著优势持续领跑中国银行业IT解决方案市场。在细分领域&am…

【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示 proteus仿真+程序+设计报告+讲解视频

【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示设计 1.主要功能&#xff1a;讲解视频&#xff1a;2.仿真3. 程序代码4. 设计报告5. 设计资料内容清单&&下载链接资料下载链接&#xff1a; 【普中】基于51单片机的矩阵电子密码锁LCD1602液晶显示设计 ( proteus仿真…

UV胶,它是否有毒?如同那些隐藏在黑暗中的危险之物?

UV胶&#xff0c;它是否有毒&#xff1f;如同那些隐藏在黑暗中的危险之物&#xff1f; 关于uv胶的毒性问题&#xff0c;或许我们可以这样深入探讨。UV胶&#xff0c;如同一位戴着神秘面纱的访客&#xff0c;在我们的生活中悄然出现&#xff0c;却带着诸多疑问。那么&#xff0…

怎样把视频字幕提取出来?分享4个零门槛的字幕提取工具

暑假正是弯道超车的好机会&#xff01;相信不少朋友都会选择宅在家自学网课。 不可否认的是&#xff0c;海量学习资源的确可以让学习变得更加便捷与自由。然而&#xff0c;如何高效地吸收和理解在线课程也就成为了一个关键问题。不敢想倘若此时能够拥有一款高效又实用的视频提…

菜花插画:成都亚恒丰创教育科技有限公司

菜花插画&#xff1a;田园诗意的视觉盛宴 在纷扰繁杂的都市生活中&#xff0c;人们往往渴望一抹清新与宁静&#xff0c;以慰藉心灵的疲惫。而菜花插画&#xff0c;恰似一股来自乡野的清风&#xff0c;以其独特的田园诗意&#xff0c;成都亚恒丰创教育科技有限公司为我们的视觉…

结构体案例1

代码 #include <iostream> using namespace std; #include <string> #include <ctime>//学生的结构体 struct Student {string sName;int score; }; //老师的结构体定义 struct Teacher {string tName;struct Student sArray[5]; };//给老师和学生赋值的函数…

HTML5使用<progress>进度条、<meter>刻度条

1、<progress>进度条 定义进度信息使用的是 progress 标签。它表示一个任务的完成进度&#xff0c;这个进度可以是不确定的&#xff0c;只是表示进度正在进行&#xff0c;但是不清楚还有多少工作量没有完成&#xff0c;也可以用0到某个最大数字&#xff08;如&#xff1…

在网上申请流量卡审核失败,可能是你的年龄有问题!

在网上申请流量卡审核失败&#xff0c;可能是你的年龄有问题&#xff01; 先上个图&#xff1a; ​ 网上的流量卡并不是随意申请的&#xff0c;而是填写申请信息后由运营商进行审核&#xff0c;审核通过后才会发卡&#xff0c;如果你提交的订单没有审核通过&#xff0c;那么大…

Unity之OpenXR+XR Interaction Toolkit实现 Gaze眼部追踪

使用 Unity OpenXR 实现Gaze眼部追踪 在虚拟现实(VR)和增强现实(AR)应用中,眼动追踪是一项强大而受欢迎的技术。它可以让开发者更好地理解用户的注意力和行为,并创造出更加沉浸和智能的体验。在本文中,我们将探讨如何使用 Unity OpenXR 实现Gaze眼部追踪功能。 Unity …

MySQL体系架构解析

1.MySQL体系架构 1.1.MySQL的分支与变种 MySQL变种有好几个,主要有三个久经考验的主流变种:Percona Server,MariaDB和 Drizzle。它们都有活跃的用户社区和一些商业支持,均由独立的服务供应商支持。同时还有几个优秀的开源关系数据库,值得我们了解一下。 1.1.1.Drizzle …

RockyLinux9上安装Nacos2.3.0(非Docker安装)

RockyLinux9上安装Nacos2.3.0 说明什么是Nacos下载并安装创建一个nacos-conf数据库修改application.properties文件 启动如果在项目中使用需要注意访问网址查看是否成功 开启访问鉴权 说明 本文采用的是&#xff1a;安装包安装&#xff0c;非Docker安装&#xff0c;系统采用的R…

雷池WAF动态防护功能初体验

一、 介绍 大名鼎鼎的雷池WAF最近新上了个名为 动态防护 的功能 所谓动态防护&#xff0c;是在用户浏览到的网页内容不变的情况下&#xff0c;将网页赋予动态特性&#xff0c;即使是静态页面&#xff0c;也会具有动态的随机性。 说白了就是给你网站的 html 和 js 代码加上加密…

Linux下为什么ls直接就可以运行,而你的程序要写./dir1/dir2/bin/bwa才可以

习惯了Windows电脑下的所见即所得&#xff0c;找到程序或文件双击即可运行或打开&#xff1b;于是我们被惯得以为电脑会像人一样聪明&#xff0c;给他一个名字就可以运行程序或打开文件&#xff1b;于是在命令行下或程序里不断碰壁&#xff0c;为啥这个命令不运行了呢&#xff…