目标和

悠悠 2023-02-23 09:25 199阅读 0赞

一、前言

动态规划相关问题。

问题来源LeetCode 494

问题链接:https://leetcode-cn.com/problems/target-sum

二、题目

给定一个非负整数数组,a1, a2, …, an,和一个目标数,S。现在你有两个符号 + 和 -。对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。返回可以使最终数组和为目标数 S 的所有添加符号的方法数。

示例:

  1. 输入:nums: [1, 1, 1, 1, 1], S: 3
  2. 输出:5
  3. 解释:
  4. -1+1+1+1+1 = 3
  5. +1-1+1+1+1 = 3
  6. +1+1-1+1+1 = 3
  7. +1+1+1-1+1 = 3
  8. +1+1+1+1-1 = 3
  9. 一共有5种方法让最终目标和为3

提示:

  • 数组非空,且长度不会超过 20 。
  • 初始的数组的和不会超过 1000 。
  • 保证返回的最终结果能被 32 位整数存下。

三、思路

这里提供了两种解法,一种是暴力法,直接看代码即可;一种是转换成01背包问题。转换过程:设数组中数据符号是+和是A,符合是-的和的绝对值是B,这组数字绝对值相加和为C。

A + B = C;A - B = S => A+B+A-B = C+S => 2A = C+S => A = (C+S)/2;C+S需要是偶数,我们只需要在数组中找到和为A就满足条件。

3.1 递推公式

  1. f(i,j) = f(i,j) + f(i, j - a[i]); (j = [S,a[i]])
  2. f(0,0) = 1;

3.2 图解实例

实例 a = { 2,3,4,2,2,3 }, S= -4,求输出方法数?

创建一个数组dp[7]用于记录,满足目标的个数。为了方便计算我们将dp[0]设置为1。计算过程我们是从后往前计算。

1)只有一个数据a1=2

20200707001821200.png

2)新加一个数据a2=3

20200707001838584.png

3)新加一个数据a3=4

20200707001856548.png

4)新加一个数据a4=2

20200707001919684.png

5)新加一个数据a5=2

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25pZTIzMTQ1NTA0NDE_size_16_color_FFFFFF_t_70

6)新加一个数据a6=3

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L25pZTIzMTQ1NTA0NDE_size_16_color_FFFFFF_t_70 1

最终结果是5。

四、总结

  1. 理解转换过程;
  2. 每加一个是数据,计算过程是从后往前计算;

五、编码实现

  1. //==========================================================================
  2. /**
  3. * @file : 494_FindTargetSumWays.h
  4. * @blogs :
  5. * @author : niebingyu
  6. * @date :2020/07/06
  7. * @title : 目标和
  8. * @purpose : 给定一个非负整数数组,a1, a2, ..., an, 和一个目标数,S。现在你有两个符号 + 和 -。
  9. * 对于数组中的任意一个整数,你都可以从 + 或 -中选择一个符号添加在前面。
  10. * 返回可以使最终数组和为目标数 S 的所有添加符号的方法数。
  11. *
  12. * 示例:
  13. *
  14. * 输入:nums: [1, 1, 1, 1, 1], S: 3
  15. * 输出:5
  16. * 解释:
  17. * -1+1+1+1+1 = 3
  18. * +1-1+1+1+1 = 3
  19. * +1+1-1+1+1 = 3
  20. * +1+1+1-1+1 = 3
  21. * +1+1+1+1-1 = 3
  22. *
  23. * 一共有5种方法让最终目标和为3。
  24. *
  25. * 提示:
  26. * 数组非空,且长度不会超过 20 。
  27. * 初始的数组的和不会超过 1000 。
  28. * 保证返回的最终结果能被 32 位整数存下。
  29. *
  30. * 来源:力扣(LeetCode)
  31. * 难度:困难
  32. * 链接:https://leetcode-cn.com/problems/target-sum
  33. */
  34. //==========================================================================
  35. #pragma once
  36. #include <iostream>
  37. #include <vector>
  38. #include <list>
  39. #include <algorithm>
  40. using namespace std;
  41. #define NAMESPACE_FINDTARGETSUMWAYS namespace NAME_FINDTARGETSUMWAYS {
  42. #define NAMESPACE_FINDTARGETSUMWAYSEND }
  43. NAMESPACE_FINDTARGETSUMWAYS
  44. // 暴力解法
  45. class Solution2
  46. {
  47. public:
  48. int findTargetSumWays(vector<int>& nums, int S)
  49. {
  50. return dfs(nums, S, 0);
  51. }
  52. int dfs(vector<int>& nums, int target, int left)
  53. {
  54. if (target == 0 && left == nums.size())
  55. return 1;
  56. if (left >= nums.size())
  57. return 0;
  58. int ans = 0;
  59. ans += dfs(nums, target - nums[left], left + 1);
  60. ans += dfs(nums, target + nums[left], left + 1);
  61. return ans;
  62. }
  63. };
  64. // 动态规划解法
  65. class Solution
  66. {
  67. public:
  68. int findTargetSumWays(vector<int>& nums, int S)
  69. {
  70. long sum = 0;
  71. for (const int& n : nums)
  72. sum += n;
  73. if ((S + sum) % 2 == 1 || S > sum)
  74. return 0;
  75. S = (S + sum) / 2;
  76. vector<int> dp(S + 1, 0);
  77. dp[0] = 1;
  78. for (const int& n : nums)
  79. {
  80. for (int j = S; j >= n; j--)
  81. {
  82. dp[j] += dp[j - n];
  83. }
  84. }
  85. int ans = dp[S];
  86. return ans;
  87. }
  88. };
  89. 以下为测试代码//
  90. // 测试 用例 START
  91. void test(const char* testName, vector<int>& nums, int S, int expect)
  92. {
  93. Solution s;
  94. int result = s.findTargetSumWays(nums, S);
  95. if (expect == result)
  96. {
  97. cout << testName << ", solution passed." << endl;
  98. }
  99. else
  100. {
  101. cout << testName << ", solution failed." << endl;
  102. }
  103. }
  104. void Test1()
  105. {
  106. vector<int>nums = { 2,3,4,2,2,3 };
  107. int S = -4;
  108. int expect = 5;
  109. test("Test1()", nums, S, expect);
  110. }
  111. NAMESPACE_FINDTARGETSUMWAYSEND
  112. // 测试 用例 END
  113. //
  114. void FindTargetSumWays_Test()
  115. {
  116. NAME_FINDTARGETSUMWAYS::Test1();
  117. }

执行结果:

20200707002139845.png

发表评论

表情:
评论列表 (有 0 条评论,199人围观)

还没有评论,来说两句吧...

相关阅读

    相关 494.目标

    ![在这里插入图片描述][67f4b8e3f6ed4ccd9f395c038504f144.png] 1. 回溯算法 这题和之前做的那些排列、组合的回溯稍微有些不同,你

    相关 目标

    一、前言 动态规划相关问题。 问题来源LeetCode 494 问题链接:[https://leetcode-cn.com/problems/target-sum][

    相关 494. 目标

    给你一个整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 : 例如,nums =