Skip to content

Commit 6d6f019

Browse files
committed
Update 0494. 目标和.md
1 parent f5c6502 commit 6d6f019

File tree

1 file changed

+138
-6
lines changed

1 file changed

+138
-6
lines changed

Solutions/0494. 目标和.md

Lines changed: 138 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,152 @@
55

66
## 题目大意
77

8-
给定一个整数数组 `nums` 和一个整数 `target`。数组长度不超过 20。向数组中每个整数前加 `+``-`。然后串联起来构造成一个表达式。
8+
**描述**给定一个整数数组 `nums` 和一个整数 `target`。数组长度不超过 `20`。向数组中每个整数前加 `+``-`。然后串联起来构造成一个表达式。
99

10-
要求:返回通过上述方法构造的、运算结果等于 `target` 的不同表达式数目。
10+
**要求**:返回通过上述方法构造的、运算结果等于 `target` 的不同表达式数目。
11+
12+
**说明**
13+
14+
- $1 \le nums.length \le 20$。
15+
- $0 \le nums[i] \le 1000$。
16+
- $0 \le sum(nums[i]) \le 1000$。
17+
- $-1000 \le target \le 1000$。
18+
19+
**示例**
20+
21+
```Python
22+
输入:nums = [1,1,1,1,1], target = 3
23+
输出:5
24+
解释:一共有 5 种方法让最终目标和为 3
25+
-1 + 1 + 1 + 1 + 1 = 3
26+
+1 - 1 + 1 + 1 + 1 = 3
27+
+1 + 1 - 1 + 1 + 1 = 3
28+
+1 + 1 + 1 - 1 + 1 = 3
29+
+1 + 1 + 1 + 1 - 1 = 3
30+
31+
32+
输入:nums = [1], target = 1
33+
输出:1
34+
```
1135

1236
## 解题思路
1337

14-
暴力方法就是使用深度优先搜索对每位数字遍历 `+``-`,并统计符合要求的表达式数目。但是实际发现超时了。所以采用动态规划的方法来做。
38+
### 思路 1:深度优先搜索(超时)
39+
40+
使用深度优先搜索对每位数字进行 `+` 或者 `-`,具体步骤如下:
41+
42+
1. 定义从位置 `0`、和为 `0` 开始,到达数组尾部位置为止,和为 `target` 的方案数为 `dfs(0, 0)``size`
43+
2. 下面从位置 `0`、和为 `0` 开始,以深度优先搜索遍历每个位置。
44+
3. 如果当前位置 `i` 到达最后一个位置 `size`
45+
1. 如果和 `cur_sum` 等于目标和 `target`,则返回方案数 `1`
46+
2. 如果和 `cur_sum` 不等于目标和 `target`,则返回方案数 `0`
47+
4. 递归搜索 `i + 1` 位置,和为 `cur_sum - nums[i]` 的方案数。
48+
5. 递归搜索 `i + 1` 位置,和为 `cur_sum + nums[i]` 的方案数。
49+
6. 将 4 ~ 5 两个方案数加起来就是当前位置 `i`、和为 `cur_sum` 的方案数,返回该方案数。
50+
7. 最终方案数为 `dfs(0, 0)`,将其作为答案返回即可。
51+
52+
### 思路 1:代码
53+
54+
```Python
55+
class Solution:
56+
def findTargetSumWays(self, nums: List[int], target: int) -> int:
57+
size = len(nums)
58+
59+
def dfs(i, cur_sum):
60+
if i == size:
61+
if cur_sum == target:
62+
return 1
63+
else:
64+
return 0
65+
ans = dfs(i + 1, cur_sum - nums[i]) + dfs(i + 1, cur_sum + nums[i])
66+
return ans
67+
68+
return dfs(0, 0)
69+
```
70+
71+
### 思路 1:复杂度分析
72+
73+
- **时间复杂度**:$O(2^n)$。其中 $n$ 为数组 $nums$ 的长度。
74+
- **空间复杂度**:$O(n)$。递归调用的栈空间深度不超过 $n$。
75+
76+
### 思路 2:深度优先搜索 + 记忆化搜索
77+
78+
在思路 1 中我们单独使用深度优先搜索对每位数字进行 `+` 或者 `-` 的方法超时了。所以我们考虑使用记忆化搜索的方式,避免进行重复搜索。
79+
80+
这里我们使用哈希表 `table` 记录遍历过的位置 `i` 及所得到的的当前和`cur_sum` 下的方案数,来避免重复搜索。具体步骤如下:
81+
82+
1. 定义从位置 `0`、和为 `0` 开始,到达数组尾部位置为止,和为 `target` 的方案数为 `dfs(0, 0)`
83+
2. 下面从位置 `0`、和为 `0` 开始,以深度优先搜索遍历每个位置。
84+
3. 如果当前位置 `i` 遍历完所有位置:
85+
1. 如果和 `cur_sum` 等于目标和 `target`,则返回方案数 `1`
86+
2. 如果和 `cur_sum` 不等于目标和 `target`,则返回方案数 `0`
87+
4. 如果当前位置 `i`、和为 `cur_sum` 之前记录过(即使用 `table` 记录过对应方案数),则返回该方案数。
88+
5. 如果当前位置 `i`、和为 `cur_sum` 之前没有记录过,则:
89+
1. 递归搜索 `i + 1` 位置,和为 `cur_sum - nums[i]` 的方案数。
90+
2. 递归搜索 `i + 1` 位置,和为 `cur_sum + nums[i]` 的方案数。
91+
3. 将上述两个方案数加起来就是当前位置 `i`、和为 `cur_sum` 的方案数,将其记录到哈希表 `table` 中,并返回该方案数。
92+
6. 最终方案数为 `dfs(0, 0)`,将其作为答案返回即可。
93+
94+
### 思路 2:代码
95+
96+
```Python
97+
class Solution:
98+
def findTargetSumWays(self, nums: List[int], target: int) -> int:
99+
size = len(nums)
100+
table = dict()
101+
102+
def dfs(i, cur_sum):
103+
if i == size:
104+
if cur_sum == target:
105+
return 1
106+
else:
107+
return 0
108+
109+
if (i, cur_sum) in table:
110+
return table[(i, cur_sum)]
111+
112+
cnt = dfs(i + 1, cur_sum - nums[i]) + dfs(i + 1, cur_sum + nums[i])
113+
table[(i, cur_sum)] = cnt
114+
return cnt
115+
116+
return dfs(0, 0)
117+
```
118+
119+
### 思路 2:复杂度分析
120+
121+
- **时间复杂度**:$O(2^n)$。其中 $n$ 为数组 $nums$ 的长度。
122+
- **空间复杂度**:$O(n)$。递归调用的栈空间深度不超过 $n$。
123+
124+
### 思路 3:动态规划
15125

16126
假设数组中所有元素和为 `sum`,数组中所有符号为 `+` 的元素为 `sum_x`,符号为 `-` 的元素和为 `sum_y`。则 `target = sum_x - sum_y`
17127

18128
`sum_x + sum_y = sum`。根据两个式子可以求出 `2 * sum_x = target + sum `,即 `sum_x = (target + sum) / 2`
19129

20130
那么这道题就变成了,如何在数组中找到一个集合,使集合中元素和为 `(target + sum) / 2`。这就变为了求容量为 `(target + sum) / 2` 的 01 背包问题。
21131

22-
动态规划的状态 `dp[i]` 表示为:填满容量为 `i` 的背包,有 `dp[i]` 种方法。
132+
###### 1. 定义状态
133+
134+
定义状态 `dp[i]` 表示为:填满容量为 `i` 的背包,有 `dp[i]` 种方法。
135+
136+
###### 2. 状态转移方程
137+
138+
填满容量为 `i` 的背包的方法数来源于:
139+
140+
1. 不使用当前 `num`:只使用之前元素填满容量为 `i` 的背包的方法数。
141+
2. 使用当前 `num`:填满容量 `i - num` 的包的方法数,再填入 `num` 的方法数。
142+
143+
则动态规划的状态转移方程为:`dp[i] = dp[i] + dp[i - num]`
23144

24-
动态规划的状态转移方程为:`dp[i] = dp[i] + dp[i - num]`,意思为填满容量为 `i` 的背包的方法数 = 不使用当前 `num`,只使用之前元素填满容量为 `i` 的背包的方法数 + 填满容量 `i - num` 的包的方法数,再填入 `num` 的方法数。
145+
###### 3. 初始化
25146

26-
## 代码
147+
初始状态下,默认填满容量为 `0` 的背包有 `1` 种办法。即 `dp[i] = 1`
148+
149+
###### 4. 最终结果
150+
151+
根据状态定义,最后输出 `dp[sise]`(即填满容量为 `size` 的背包,有 `dp[size]` 种方法)即可,其中 `size` 为数组 `nums` 的长度。
152+
153+
### 思路 3:代码
27154

28155
```Python
29156
class Solution:
@@ -40,3 +167,8 @@ class Solution:
40167
return dp[size]
41168
```
42169

170+
### 思路 3:复杂度分析
171+
172+
- **时间复杂度**:$O(n)$。$n$ 为数组 $nums$ 的长度。
173+
- **空间复杂度**:$O(n)$。
174+

0 commit comments

Comments
 (0)