From 48ed268adefcf0653b315ba20f0bc94f6d535f14 Mon Sep 17 00:00:00 2001 From: tokuhirat <54652919+tokuhirat@users.noreply.github.com> Date: Sun, 17 Aug 2025 13:03:31 +0900 Subject: [PATCH] Create 39. Combination Sum.md --- 39. Combination Sum/39. Combination Sum.md | 130 +++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 39. Combination Sum/39. Combination Sum.md diff --git a/39. Combination Sum/39. Combination Sum.md b/39. Combination Sum/39. Combination Sum.md new file mode 100644 index 0000000..2eae273 --- /dev/null +++ b/39. Combination Sum/39. Combination Sum.md @@ -0,0 +1,130 @@ +# 39. Combination Sum +## STEP1 +- 何も見ずに解いてみる +- 78\. Subsets では各要素を使う使わないの2通りだったが、この問題は各要素を使えるだけ使って良い。 +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + results = [] + + def generate_combination(index: int, combination: list[int]) -> None: + total = sum(combination) + if total == target: + results.append(combination) + return + if index == len(candidates): + return + + generate_combination(index + 1, combination[:]) + num = candidates[index] + while total + num <= target: + combination = combination + [num] + total += num + generate_combination(index + 1, combination) + + generate_combination(0, []) + return results +``` +- generate_combination の引数 combination は毎回異なるオブジェクトを指すようにした。コピーしない場合は while:pop みたいなことをする必要があり煩雑になると思った。 +- generate_combination 内で if total > target: return を書くか迷った。 +- generate_combination の呼び出しをまとめようとすると以下になる。こちらの方がすこしわかりやすいかもしれない。 +```python + next_combination = combination[:] + num = candidates[index] + while total <= target: + generate_combination(index + 1, next_combination) + next_combination = next_combination + [num] + total += num +``` + +- DP でも書いてみた。 +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + sum_to_combinations = [[] for _ in range(target + 1)] + sum_to_combinations[0].append([]) + for sum_ in range(target + 1): + if not sum_to_combinations[sum_]: + continue + for combination in sum_to_combinations[sum_]: + for num in candidates: + if combination and combination[-1] >= num: + continue + for i in range(1, (target - sum_) // num + 1): + sum_to_combinations[sum_ + num * i].append( + combination + [num] * i + ) + return sum_to_combinations[target] +``` +- if not sum_to_combinations[sum_]:continue は不要でした。 + +## STEP2 +### プルリクやドキュメントを参照 +- https://github.com/olsen-blue/Arai60/pull/53/files + - 解法1 が結構読みにくかった。traverse_candidates が index を進めない場合があることを最初読めていなくて、index == len(candidates) を先に確認したらダメなのではないかと考えてしまった。 + - シンプルに DP を実装している。 + - 一番外側で使える数字ごとにループを回すと、重複を考慮しないで済む。 +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + sum_to_combinations = [[] for _ in range(target + 1)] + sum_to_combinations[0].append([]) + for candidate in candidates: + for sum_ in range(candidate, target + 1): + for combination in sum_to_combinations[sum_ - candidate]: + sum_to_combinations[sum_].append(combination + [candidate]) + return sum_to_combinations[target] +``` +- https://discord.com/channels/1084280443945353267/1235829049511903273/1265717341178822787 + - 分岐の分類。色々ありますね。 +- stack でも書いてみる。 +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + results = [] + combination_and_index = [([], 0)] + + while combination_and_index: + combination, index = combination_and_index.pop() + if sum(combination) == target: + results.append(combination) + continue + if index >= len(candidates): + continue + + num = candidates[index] + sum_ = sum(combination) + new_combination = combination[:] + while sum_ <= target: + combination_and_index.append((new_combination[:], index + 1)) + sum_ += num + new_combination.append(num) + return results +``` +## STEP3 +### 3回ミスなく書く +```python +class Solution: + def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: + results = [] + + def generate_combination(index: int, combination: list[int]) -> None: + if sum(combination) == target: + results.append(combination) + return + if index >= len(candidates): + return + + sum_ = sum(combination) + num = candidates[index] + new_combination = combination[:] + while sum_ <= target: + generate_combination(index + 1, new_combination[:]) + sum_ += num + new_combination.append(num) + + generate_combination(0, []) + return results +``` + +3分,3分,3分で3回Accept