-
Notifications
You must be signed in to change notification settings - Fork 0
39. Combination Sum #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| ### Step1 | ||
|
|
||
| - 再帰やstackでも解けそうだが、手でやるならcandidates配列をfor文で舐めるので、そのような書き方にしてみた | ||
| - 思ったよりややこしくなってしまった | ||
| - 変数名もいまいちな気もする | ||
| - ネストが深い気もするが、これ以上どうしようもないよなあ | ||
| - 「各配列の値について、今まで追加した要素についてそれぞれ、配列の値をできる限り追加」なのでややこしい | ||
| - コピーが結構発生してしまうが、これもしょうがない | ||
| - これだとキツくて、やっぱstackとか使った方が良かったか、、、 | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| all_nums_and_sum = [] | ||
| sum_is_target = [] | ||
| all_nums_and_sum.append(([], 0)) | ||
| for candidate in candidates: | ||
| added_nums_and_sum = [] | ||
| for current_nums, current_sum in all_nums_and_sum: | ||
| added_nums = current_nums.copy() | ||
| added_sum = current_sum | ||
| while True: | ||
| if added_sum + candidate > target: | ||
| break | ||
| if added_sum + candidate == target: | ||
| sum_is_target.append(added_nums + [candidate]) | ||
| break | ||
| added_nums += [candidate] | ||
| added_sum += candidate | ||
| added_nums_and_sum.append((added_nums.copy(), added_sum)) | ||
| all_nums_and_sum += added_nums_and_sum | ||
| return sum_is_target | ||
| ``` | ||
|
|
||
| ### Step2 | ||
|
|
||
| - Stackでの解法 | ||
| - 後で、stackに入れる方法をうまくやればwhile True: のネストはいらないことに気づく | ||
| - ただwhile Trueの方が手でやる操作としては素直な気もする | ||
| - まだ変数名はいまいちなきが(後で直す) | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| nums_index_sum_stack = [([], 0, 0)] | ||
| all_combinations = [] | ||
| while nums_index_sum_stack: | ||
| nums, index, current_sum = nums_index_sum_stack.pop() | ||
| if index >= len(candidates): | ||
| continue | ||
| while True: | ||
| if current_sum > target: | ||
| break | ||
| if current_sum == target: | ||
| all_combinations.append(nums.copy()) | ||
| break | ||
| nums_index_sum_stack.append((nums.copy(), index + 1, current_sum)) | ||
| nums.append(candidates[index]) | ||
| current_sum += candidates[index] | ||
| return all_combinations | ||
| ``` | ||
|
|
||
| - 再帰での解法 | ||
| - どうもネストが深くなる | ||
| - 今回はwhile Trueより,条件を横に置いた方がいいと思いこうした | ||
| - まだ変数名はいまいちな気がする(後で直す) | ||
|
|
||
| ```python | ||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
|
|
||
| # 現在の要素を含む、和がtarget以下の組み合わせと、その和を列挙 | ||
| def enumerate_sum_under_target_after_index(index): | ||
| all_nums_and_sum = [] | ||
| if index == len(candidates): | ||
| return [([], 0)] | ||
| for i in range(index + 1, len(candidates) + 1): | ||
| for current_nums, current_sum in enumerate_sum_under_target_after_index(i): | ||
| while current_sum + candidates[index] <= target: | ||
| current_nums.append(candidates[index]) | ||
| current_sum += candidates[index] | ||
| all_nums_and_sum.append((current_nums.copy(), current_sum)) | ||
| return all_nums_and_sum | ||
|
|
||
| target_combinations = [] | ||
| for i in range(len(candidates)): | ||
| for current_nums, current_sum in enumerate_sum_under_target_after_index(i): | ||
| if current_sum == target: | ||
| target_combinations.append(current_nums) | ||
| return target_combinations | ||
|
|
||
| ``` | ||
|
|
||
| https://github.com/Exzrgs/LeetCode/pull/13/files#diff-5ea0de015187769c7eee5e7ab623e2bc75c88a7c051921b35d5d5826aa0ebb26R35 | ||
|
|
||
| https://github.com/SuperHotDogCat/coding-interview/pull/11/files | ||
|
|
||
| - 結構みんなbacktrackで書いてるなあ(backtrackが個人的にしっくり来てないんけど、一応練習した方がいいのかな) | ||
| - stackや再帰での引き継ぎの選択肢として、自分のStep1では「今のindexをできるだけ入れて次のindexへ」とやったが | ||
| - 今の値を1個入れて今のindexに留まるものと、何も入れずに次のindexに行くものを用意 | ||
| - 今の値から最後の値まで1個ずつ入れたものをそれぞれ用意(これはちょっと不自然な気がした) | ||
| - 変数名に関する議論。_so_farというのは良いね。 | ||
| - 時間計算量、空間計算量 | ||
| - [これ](https://github.com/Mike0121/LeetCode/pull/1#discussion_r1578068513)によると、答えの数は分割数だが、(nums, index, sum)のsum<kのペアの数(と合計した空間計算量)はどうなるかというのはちょっと考えたけどわからない | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1200089668901937312/1202214255689203732 | ||
|
|
||
| - この辺の議論を見る。再帰関数の引数としてremainingも入れる選択肢もあるが、[ここ](https://github.com/Mike0121/LeetCode/pull/1/files)には引数が少ない方がいいと書いてあったのでやめた | ||
| - targetを超えるまで押し込むときにwhile True: は気持ちとしてはそうだが嫌ですねと書かれていた^^; https://discord.com/channels/1084280443945353267/1200089668901937312/1202214255689203732 | ||
| - ということで今までのを踏まえ、以下のように書く | ||
| - 変数名は、伝えたい中核メッセージを頭の中ではっきりさせてから決めると、いいかんじになるのかも | ||
| - 例えば、stackはstackであることが一番伝えたくて、numとindexとsumが入ってて〜というのはすぐ後を見ればまあわかる | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| stack= [([], 0, 0)] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 細かいですが、 |
||
| sum_is_target = [] | ||
| while stack: | ||
| nums_so_far, index, sum_so_far = stack.pop() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if index == len(candidates): | ||
| continue | ||
| if sum_so_far > target: | ||
| continue | ||
| if sum_so_far == target: | ||
| sum_is_target.append(nums_so_far) | ||
| continue | ||
| stack.append((nums_so_far, index + 1, sum_so_far)) | ||
| stack.append((nums_so_far + [candidates[index]], index, sum_so_far + candidates[index])) | ||
| return sum_is_target | ||
| ``` | ||
|
|
||
| backtrackも一応練習、上のstackを書いた後だとちょっとはしっくりくるかも | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| combination_target_sum = [] | ||
| combination_searched = [] | ||
|
|
||
| def make_combination(index, combination_sum): | ||
| if index == len(candidates): | ||
| return | ||
| if combination_sum > target: | ||
| return | ||
| if combination_sum == target: | ||
| combination_target_sum.append(combination_searched.copy()) | ||
| return | ||
| make_combination(index + 1, combination_sum) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 組み合わせになり得るかの計算までに都度 複数回の再帰が必要で、動きを追いかけることが個人的に難しいと感じました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。
これは再帰を使わないで書く、ということですか。いまいち方法が思いつかず、、、 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。
はい、下記のようなイメージでした。 def make_combination(start, combination_sum):
for index in range(start, len(candidates)):
if combination_sum > target:
return
if combination_sum == target:
combination_target_sum.append(combination_searched.copy())
return
combination_searched.append(candidates[index])
make_combination(index, combination_sum + candidates[index])
combination_searched.pop()
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど、理解しました。コードまでありがとうございます。 |
||
| combination_searched.append(candidates[index]) | ||
| make_combination(index, combination_sum + candidates[index]) | ||
| combination_searched.pop() | ||
|
|
||
| make_combination(0, 0) | ||
| return combination_target_sum | ||
| ``` | ||
|
|
||
| ### Step3 | ||
|
|
||
| ```python | ||
|
|
||
| class Solution: | ||
| def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]: | ||
| stack= [([], 0, 0)] | ||
| sum_is_target = [] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. step2のall_combinationsの方が個人的に好みです。sum_is_targetだと僕は英語のSVC構文のように, 見えてしまいます。 |
||
| while stack: | ||
| nums_so_far, index, sum_so_far = stack.pop() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. これも細かくてすみませんが、 |
||
| if index == len(candidates): | ||
| continue | ||
| if sum_so_far > target: | ||
| continue | ||
| if sum_so_far == target: | ||
| sum_is_target.append(nums_so_far) | ||
| continue | ||
| stack.append((nums_so_far, index + 1, sum_so_far)) | ||
| stack.append((nums_so_far + [candidates[index]], index, sum_so_far + candidates[index])) | ||
| return sum_is_target | ||
| ``` | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Step1のものにレビューするのも少しあれですが...)numsよりかはcombinationsでしょうか, 問題文や関数にあるものを命名に使うといい感じになる気がします。