Leetcode-494-Target-Sum

本文详细解析了LeetCode上第494题“目标和”的解题思路,通过数学推导将问题转化为0-1背包问题,使用动态规划解决。文章提供了Java实现代码及提交结果。

Leetcode-494-Target-Sum

题目如下:

You are given a list of non-negative integers, a1, a2, …, an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol.
Find out how many ways to assign symbols to make sum of integers equal to target S.

Example 1:

 

1

2

3

4

5

6

7

8

9

10

 

> Input: nums is [1, 1, 1, 1, 1], S is 3.

> Output: 5

> Explanation:

> -1+1+1+1+1 = 3

> +1-1+1+1+1 = 3

> +1+1-1+1+1 = 3

> +1+1+1-1+1 = 3

> +1+1+1+1-1 = 3

> There are 5 ways to assign symbols to make the sum of nums be target 3.

>

 

Note:

  1. The length of the given array is positive and will not exceed 20.
  2. The sum of elements in the given array will not exceed 1000.
  3. Your output answer is guaranteed to be fitted in a 32-bit integer.

题目大意

给定一个正数数组以及一个整数S,现在有两个符号+-,对于数组里面的每一个数字,你可以选择+或者-作为它的新符号,找出所有选择符号的方式,使这些数字和为S。简单来说,给定一个数组,每个数可以加或者减,求出所有能使这些数字加减和为S的组合数。如{1,1,1,1,1},S为3,那么一共有5种方法使得这些数字和为3:

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

思路

假设数组中所有数字之和为sum。根据使用的符号不同,我们可以将数组内数字分为2组PN,如数组{1,2,3},目标为0,那么显然有如下选择方式:

+1+2-3 = 0

那么其中P={1,2},N={3}。那么sum(P)-sum(N)=S,并且sum(P)+sum(N)=sum。由此可以推出S+sum=sum(P)-sum(N)+sum(P)+sum(N)=2*sum(P)

这样我们就把原来的问题转换成了一个新的问题,在整个数组中,能否找到一些数字的和为(S+sum)/2,这就成了一个最经典的0-1背包问题,这样的话就简单了很多。

这里有一个需要注意的地方,(S+sum)/2这个数必须为偶数,否则无法得到,其次,S也必须小于sum,否则也无法求得。

这个算法的空间复杂度不定,取决于Ssum,不过题目限制了sum < 1000,那么空间复杂度为O(sum)。至于时间复杂度也取决于sum,时间复杂度是O(sum*N2)。Java版的代码如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

 

public int findTargetSumWays(int[] nums, int S) {

int sum = 0;

for (int i = 0; i < nums.length; i ++){

sum += nums[i];

}

if (S > sum || (S + sum) % 2 != 0){

return 0;

}

sum = (S + sum) / 2;

int [] dp = new int[sum + 1];

dp[0] = 1;

for (int i = 0; i < nums.length; i ++){

for (int j = sum; j >= nums[i]; j --){

dp[j] += dp[j - nums[i]];

}

}

return dp[sum];

}

提交结果如下:

运行结果

运行结果

 

总结

最近在刷Leetcode上的tag为动态规划的题,做了那么多其实动态规划的思想一直没变,最重要的是找到状态、状态转移公式以及转移状态的条件。当然,有的时候还需要一些数学推导,把当前不熟悉的问题转化成熟悉的问题。
```
原文链接:http://KingsFish.github.io/2017/08/22/Leetcode-494-Target-Sum/

LeetCode 中,two-sum 问题是经典的算法问题之一。使用哈希表解法是其中一种高效且常见的实现方式。该方法的时间复杂度为 O(n),空间复杂度也为 O(n),相较于暴力双重循环的 O(n²) 方法更优。 ### 哈希表解法的核心思想 通过遍历数组,在每次迭代中计算当前元素与目标值之间的差值(即 `target - nums[i]`),然后检查该差值是否已经存在于哈希表中。如果存在,则说明找到了满足条件的两个数;如果不存在,则将当前元素及其索引存入哈希表中,以便后续查找。 ### C++ 实现代码 ```cpp class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int, int> hash; // 存储元素的值下标 int n = nums.size(); for (int i = 0; i < n; ++i) { int x = target - nums[i]; // 寻找对应的另一个加数 if (hash.count(x)) return { hash[x], i }; // 如果找到,直接返回结果 else hash[nums[i]] = i; // 否则,将当前元素存入哈希表 } return { -1, -1 }; // 没有找到符合条件的两个数 } }; ``` ### C 实现代码 另一种实现方式是使用静态分配或动态分配的哈希表结构,例如使用数组模拟哈希表。这种方法在某些特定条件下可能效率更高,尤其是当输入数据范围较小且已知时。 以下是一个优化过的 C 语言实现示例: ```c int* twoSum(int* nums, int numsSize, int target, int* returnSize) { int min = INT_MAX; *returnSize = 2; int i = 0; for (i = 0; i < numsSize; i++) { if (nums[i] < min) min = nums[i]; } int max = target - min; int len = max - min + 1; // 确定哈希表长度 if (len <= 50000) { int *table = (int*)malloc(len * sizeof(int)); int *indice = (int*)malloc(2 * sizeof(int)); for (i = 0; i < len; i++) { table[i] = -1; // 初始化哈希表 } for (i = 0; i < numsSize; i++) { if (nums[i] - min < len) { if (table[target - nums[i] - min] != -1) { indice[0] = table[target - nums[i] - min]; indice[1] = i; return indice; } table[nums[i] - min] = i; } } free(table); return indice; } else { int *a = (int *)malloc(sizeof(int) * 2); for (int i = 0; i < numsSize; i++) { for (int j = 0; j < numsSize; j++) { if (i != j && nums[i] + nums[j] == target) { a[0] = i; a[1] = j; return a; } } } return NULL; } } ``` ### 关键点分析 - **时间复杂度**:O(n),因为每个元素只被处理一次。 - **空间复杂度**:O(n),用于存储哈希表。 - **适用场景**:适用于需要快速查找配对值的问题。 - **注意事项**:确保在查找过程中不会重复使用同一个元素,因此需在哈希表中保存的是之前遍历过的元素[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值