Skip to content

Commit a028935

Browse files
authored
Merge pull request #1104 from PradnyaGaitonde/PradnyaGaitonde-patch-2
Create 0039-Combination-Sum.md
2 parents 26a453a + 8157da2 commit a028935

File tree

1 file changed

+194
-0
lines changed

1 file changed

+194
-0
lines changed
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
id: combination-sum
3+
title: Combination Sum(LeetCode)
4+
sidebar_label: 0039-Combination Sum
5+
tags:
6+
- Array
7+
- Backtracking
8+
description: Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target.
9+
sidebar_position: 39
10+
---
11+
12+
## Problem Statement
13+
14+
Given an array of distinct integers `candidates` and a target integer `target`, return a list of all unique combinations of `candidates` where the chosen numbers
15+
sum to `target`. You may return the combinations in any order.
16+
17+
The same number may be chosen from `candidates` an unlimited number of times. Two combinations are unique if the
18+
frequency
19+
of at least one of the chosen numbers is different.
20+
21+
The test cases are generated such that the number of unique combinations that sum up to `target` is less than `150` combinations for the given input.
22+
23+
### Examples
24+
25+
**Example 1:**
26+
27+
```plaintext
28+
Input: candidates = [2,3,6,7], target = 7
29+
Output: [[2,2,3],[7]]
30+
Explanation:
31+
2 and 3 are candidates, and 2 + 2 + 3 = 7. Note that 2 can be used multiple times.
32+
7 is a candidate, and 7 = 7.
33+
These are the only two combinations.
34+
```
35+
36+
**Example 2:**
37+
38+
```plaintext
39+
Input: candidates = [2,3,5], target = 8
40+
Output: [[2,2,2,2],[2,3,3],[3,5]]
41+
```
42+
43+
**Example 3:**
44+
45+
```plaintext
46+
Input: candidates = [2], target = 1
47+
Output: []
48+
```
49+
50+
### Constraints
51+
52+
- `1 <= candidates.length <= 30`
53+
- `2 <= candidates[i] <= 40`
54+
- All elements of `candidates` are distinct.
55+
- `1 <= target <= 40`
56+
57+
## Solution
58+
59+
The Combination Sum problem involves finding all unique combinations in a list of candidates where the candidate numbers sum to a given target. Each number in the list may be used an
60+
unlimited number of times. Below, we discuss three approaches to solve this problem: DFS (Backtracking), Dynamic Programming (Slow), and Dynamic Programming (Fast).
61+
62+
### Approach 1: DFS (Backtracking)
63+
64+
#### Explanation:
65+
66+
1. Use a recursive function to explore all possible combinations.
67+
2. Track the current combination (`cur`) and its sum (`cur_sum`).
68+
3. If `cur_sum` exceeds the target, backtrack.
69+
4. If `cur_sum` equals the target, add the current combination to the result list.
70+
71+
#### Algorithm
72+
73+
1. Initialize an empty list `ans` to store the results.
74+
2. Define a recursive function `dfs(cur, cur_sum, idx)` to explore combinations.
75+
3. In `dfs`, if `cur_sum` exceeds the target, return.
76+
4. If `cur_sum` equals the target, add `cur` to `ans` and return.
77+
5. Loop through candidates starting from `idx`, and recursively call `dfs` with updated `cur` and `cur_sum`.
78+
6. Start the DFS with an empty combination, a sum of 0, and starting index 0.
79+
7. Return the list `ans`.
80+
81+
#### Implementation
82+
83+
```python
84+
class Solution:
85+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
86+
ans = []
87+
n = len(candidates)
88+
def dfs(cur, cur_sum, idx):
89+
if cur_sum > target: return
90+
if cur_sum == target:
91+
ans.append(cur)
92+
return
93+
for i in range(idx, n):
94+
dfs(cur + [candidates[i]], cur_sum + candidates[i], i)
95+
dfs([], 0, 0)
96+
return ans
97+
```
98+
99+
### Complexity Analysis
100+
101+
- **Time complexity**: O(N^(M/min_cand + 1)), where N is the number of candidates, M is the target, and min_cand is the
102+
smallest candidate.
103+
- **Space complexity**: O(M/min_cand), as the recursion depth can go up to the target divided by the smallest
104+
candidate.
105+
106+
### Approach 2: Dynamic Programming (Slow)
107+
108+
#### Explanation:
109+
110+
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`.
111+
2. Build the `dp` list by iterating through all possible sums up to the target.
112+
3. For each sum, update the combinations by considering each candidate.
113+
114+
#### Algorithm
115+
116+
1. Create a dictionary `idx_d` to map each candidate to its index.
117+
2. Initialize a list `dp` with empty lists for all sums from 0 to the target.
118+
3. For each sum `i` from 1 to the target:
119+
* Iterate through all previous sums `j` from 0 to `i-1`.
120+
* For each combination in `dp[j]`, update combinations in `dp[i]`.
121+
* Add combinations directly from candidates if they equal `i`.
122+
4. Return the list `dp[-1]`.
123+
124+
#### Implementation
125+
126+
```python
127+
class Solution:
128+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
129+
idx_d = {val: idx for idx, val in enumerate(candidates)}
130+
n = len(candidates)
131+
dp = [[] for _ in range(target + 1)]
132+
for i in range(1, target + 1):
133+
for j in range(i):
134+
for comb in dp[j]:
135+
start_idx = idx_d[comb[-1]]
136+
for val in candidates[start_idx:]:
137+
if val + j == i:
138+
dp[i].append(comb + [val])
139+
for candidate in candidates:
140+
if candidate == i:
141+
dp[i].append([candidate])
142+
return dp[-1]
143+
```
144+
145+
### Complexity Analysis
146+
147+
- **Time complexity**: O(MMM*N), where N is the number of candidates and M is the target.
148+
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target.
149+
150+
### Approach 3: Dynamic Programming (Fast)
151+
152+
#### Explanation:
153+
154+
1. Use a list `dp` where `dp[i]` contains combinations that sum up to `i`.
155+
2. For each candidate, update the combinations for all possible sums up to the target.
156+
157+
#### Algorithm
158+
159+
1. Initialize a list `dp` with empty lists for all sums from 0 to the target.
160+
2. For each candidate `c`:
161+
3. For each sum `i` from `c` to the target:
162+
* If `i` equals `c`, add `[c]` to `dp[i]`.
163+
* For each combination in `dp[i - c]`, add `comb + [c]` to `dp[i]`.
164+
4. Return the list `dp[-1]`.
165+
166+
#### Implementation
167+
168+
```python
169+
class Solution:
170+
def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
171+
dp = [[] for _ in range(target + 1)]
172+
for c in candidates:
173+
for i in range(c, target + 1):
174+
if i == c:
175+
dp[i].append([c])
176+
for comb in dp[i - c]:
177+
dp[i].append(comb + [c])
178+
return dp[-1]
179+
```
180+
181+
### Complexity Analysis
182+
183+
- **Time complexity**: O(MMN), where N is the number of candidates and M is the target.
184+
- **Space complexity**: O(M*M), due to the storage of combinations for each sum up to the target.
185+
186+
### Conclusion
187+
188+
In solving the Combination Sum problem:
189+
190+
1. DFS (Backtracking) is simple and intuitive but can be slow for large inputs due to its exponential time complexity.
191+
2. Dynamic Programming (Slow) uses a structured approach but has higher time complexity (O(MMM*N)) and memory usage.
192+
3. Dynamic Programming (Fast) is more efficient (O(MMN)), making it suitable for larger inputs, though it still uses significant memory.
193+
194+
Choose DFS (Backtracking) for smaller datasets, DP (Slow) for a structured approach within manageable limits, and DP (Fast) for larger datasets where efficiency is critical.

0 commit comments

Comments
 (0)