From 9562b4a1c7a5db74fcbb3fe1b4eaae22a14210c3 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Tue, 3 Mar 2026 22:16:12 -0800 Subject: [PATCH 1/6] LeetCode 416: Add TLE solution with brute-force --- leetcode/416/memo.md | 8 ++++++++ leetcode/416/step1_tle.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 leetcode/416/memo.md create mode 100644 leetcode/416/step1_tle.py diff --git a/leetcode/416/memo.md b/leetcode/416/memo.md new file mode 100644 index 0000000..94dd0a2 --- /dev/null +++ b/leetcode/416/memo.md @@ -0,0 +1,8 @@ +# Step 1 + +合計値が `sum(nums) // 2` と一致する組み合わせを見つければいい、と考えた。 +素直にやるなら、各 index においてその要素を使うか使わないかで分岐していって、subsetの合計値をチェックしていけばいいのだが... + +> `1 <= nums.length <= 200` + +という制約から、各インデックスで2つに分岐すると考えると 2^200 通り試すことになってしまう。これはかなり多めに見積もった値であり、枝刈りができるとはいえ、TLEするだろうなと予想はつく。一応実装してみたが、案の定TLEした。-> `step1_tle.py` diff --git a/leetcode/416/step1_tle.py b/leetcode/416/step1_tle.py new file mode 100644 index 0000000..2126320 --- /dev/null +++ b/leetcode/416/step1_tle.py @@ -0,0 +1,28 @@ +class Solution: + def canPartition(self, nums: list[int]) -> bool: + sum_ = sum(nums) + if sum_ % 2 == 1: + return False + + half_sum = sum_ // 2 + + found = False + + def find_target_subset(subset_sum: int, checking: int) -> None: + nonlocal found + + if checking == len(nums): + return + + if subset_sum == half_sum: + found |= True + return + + if subset_sum > half_sum: + return + + find_target_subset(subset_sum, checking + 1) + find_target_subset(subset_sum + nums[checking], checking + 1) + + find_target_subset(0, 0) + return found From 2a1d33d200b747e79f5eb0ac329e8b9dc75d79e9 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Tue, 3 Mar 2026 23:18:00 -0800 Subject: [PATCH 2/6] LeetCode 416: Add step 1 solution backward iterating --- leetcode/416/memo.md | 20 ++++++++++++++++++++ leetcode/416/step1.py | 22 ++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 leetcode/416/step1.py diff --git a/leetcode/416/memo.md b/leetcode/416/memo.md index 94dd0a2..d943d42 100644 --- a/leetcode/416/memo.md +++ b/leetcode/416/memo.md @@ -6,3 +6,23 @@ > `1 <= nums.length <= 200` という制約から、各インデックスで2つに分岐すると考えると 2^200 通り試すことになってしまう。これはかなり多めに見積もった値であり、枝刈りができるとはいえ、TLEするだろうなと予想はつく。一応実装してみたが、案の定TLEした。-> `step1_tle.py` + +何も思いつかないので、問題のTopicsを覗いてみたらDPを使うっぽい。 +TLEした方法だと、再帰関数が二つの引数 `(subset_sum, checking_index)` を必要としていて、これをメモ化したところで... と思ったのだが、後々に同じものが出てくる可能性があるか。 + +試しに @functools.cache をヘルパー関数につけてみたら、39番目のテストケースでTLE していたものが 100 番目のテストケースでMLE するようになった。高速化はできたが、記録しておかなければならないペアがとても多くなってしまうので、筋の良い方法ではなさそう。 + +少しどうにか動的計画法を使えないか考えてみたが、思いつかなかったので、LeetCodeのDiscussionを眺めて、実装した。-> `step1.py` +逆順で辿ることで、同じ要素を2回使用してしまうことを防いでいるみたいだ。なるほど。 + +まぁ、一旦こういう手法もあるんだくらいに留めて練習してみようかな。 + +# Step 2 + +[Wikipedia - Subset Sum Problem](https://en.wikipedia.org/wiki/Subset_sum_problem) + +> The subset sum problem (SSP) is a decision problem in computer science. In its most general formulation, there is a multiset *S* of integers and a target-sum *T*, and the question is to decide whether any subset of the integers sum to precisely *T*. + +> SSP is a special case of the knapsack problem and of the multiple subset sum problem. + +この問題は、各itemが最大でも一回しか使えない 0/1 Knapsack Problem に分類されるらしい。 diff --git a/leetcode/416/step1.py b/leetcode/416/step1.py new file mode 100644 index 0000000..249c7b0 --- /dev/null +++ b/leetcode/416/step1.py @@ -0,0 +1,22 @@ +class Solution: + def canPartition(self, nums: list[int]) -> bool: + sum_ = sum(nums) + if sum_ % 2 == 1: + return False + + half_sum = sum_ // 2 + + possible = [False] * (half_sum + 1) + possible[0] = True + + for num in nums: + for i in range(half_sum - 1, -1, -1): + if not possible[i]: + continue + + if i + num > half_sum: + continue + + possible[i + num] = True + + return possible[half_sum] From e487d13cbdd08a32013e0b1e791b99b0cd923932 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Wed, 4 Mar 2026 01:30:08 -0800 Subject: [PATCH 3/6] LeetCode 416: Add step 2 solution (clean for loop) --- leetcode/416/step2.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 leetcode/416/step2.cpp diff --git a/leetcode/416/step2.cpp b/leetcode/416/step2.cpp new file mode 100644 index 0000000..59abc62 --- /dev/null +++ b/leetcode/416/step2.cpp @@ -0,0 +1,27 @@ +#include +#include + +class Solution { +public: + bool canPartition(const std::vector& nums) { + int total_sum = 0; + for (const auto& num : nums) { + total_sum += num; + } + + if (total_sum % 2 == 1) { return false; } + + int half_sum = total_sum / 2; + + std::vector possible(half_sum + 1); + possible[0] = 1; + + for (const auto& num : nums) { + for (int sum = half_sum; sum >= num; --sum) { + possible[sum] |= possible[sum - num]; + } + } + + return possible[half_sum]; + } +}; From 718ed918fb406dab2630cfc664c88d8d133661bb Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Wed, 4 Mar 2026 01:32:35 -0800 Subject: [PATCH 4/6] LeetCode 416: Escape code --- leetcode/416/memo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/leetcode/416/memo.md b/leetcode/416/memo.md index d943d42..25ea92f 100644 --- a/leetcode/416/memo.md +++ b/leetcode/416/memo.md @@ -10,7 +10,7 @@ 何も思いつかないので、問題のTopicsを覗いてみたらDPを使うっぽい。 TLEした方法だと、再帰関数が二つの引数 `(subset_sum, checking_index)` を必要としていて、これをメモ化したところで... と思ったのだが、後々に同じものが出てくる可能性があるか。 -試しに @functools.cache をヘルパー関数につけてみたら、39番目のテストケースでTLE していたものが 100 番目のテストケースでMLE するようになった。高速化はできたが、記録しておかなければならないペアがとても多くなってしまうので、筋の良い方法ではなさそう。 +試しに `@functools.cache` をヘルパー関数につけてみたら、39番目のテストケースでTLE していたものが 100 番目のテストケースでMLE するようになった。高速化はできたが、記録しておかなければならないペアがとても多くなってしまうので、筋の良い方法ではなさそう。 少しどうにか動的計画法を使えないか考えてみたが、思いつかなかったので、LeetCodeのDiscussionを眺めて、実装した。-> `step1.py` 逆順で辿ることで、同じ要素を2回使用してしまうことを防いでいるみたいだ。なるほど。 From 25c1f4d45ff4ea089d61613aa3531688f5409da0 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Wed, 4 Mar 2026 02:13:37 -0800 Subject: [PATCH 5/6] LeetCode 416: Add step 3 solution --- leetcode/416/step3.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 leetcode/416/step3.cpp diff --git a/leetcode/416/step3.cpp b/leetcode/416/step3.cpp new file mode 100644 index 0000000..f575de7 --- /dev/null +++ b/leetcode/416/step3.cpp @@ -0,0 +1,24 @@ +#include +#include + +class Solution { +public: + bool canPartition(const std::vector& nums) { + int total_sum = 0; + for (const auto& num : nums) { + total_sum += num; + } + if (total_sum % 2 == 1) { return false; } + + int half_sum = total_sum / 2; + std::vector possible(half_sum + 1); + possible[0] = 1; + for (const auto& num : nums) { + for (int sum = half_sum; sum >= num; --sum) { + possible[sum] |= possible[sum - num]; + } + } + + return possible[half_sum]; + } +}; From 86aa67c49cc4b736d5175395587dbbfe15aa6f49 Mon Sep 17 00:00:00 2001 From: Kazuki Kijima Date: Fri, 6 Mar 2026 16:08:34 -0800 Subject: [PATCH 6/6] LeetCode 416: Add step 4 solution - pass by value and prune operations --- leetcode/416/memo.md | 6 +++++ .../416/step4_return_early_pass_by_value.cpp | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 leetcode/416/step4_return_early_pass_by_value.cpp diff --git a/leetcode/416/memo.md b/leetcode/416/memo.md index 25ea92f..a346434 100644 --- a/leetcode/416/memo.md +++ b/leetcode/416/memo.md @@ -26,3 +26,9 @@ TLEした方法だと、再帰関数が二つの引数 `(subset_sum, checking_in > SSP is a special case of the knapsack problem and of the multiple subset sum problem. この問題は、各itemが最大でも一回しか使えない 0/1 Knapsack Problem に分類されるらしい。 + +# Step 4 + +[https://github.com/hemispherium/LeetCode\_Arai60/pull/10#discussion\_r2618523247](https://github.com/hemispherium/LeetCode_Arai60/pull/10#discussion_r2618523247) + +いつでも参照渡しが効率的なわけではない :eyes: diff --git a/leetcode/416/step4_return_early_pass_by_value.cpp b/leetcode/416/step4_return_early_pass_by_value.cpp new file mode 100644 index 0000000..e3565c3 --- /dev/null +++ b/leetcode/416/step4_return_early_pass_by_value.cpp @@ -0,0 +1,27 @@ +#include +#include + +class Solution { +public: + bool canPartition(const std::vector& nums) { + int total_sum = 0; + for (int num : nums) { + total_sum += num; + } + if (total_sum % 2 == 1) { return false; } + + int half_sum = total_sum / 2; + std::vector possible(half_sum + 1); + possible[0] = true; + for (int num : nums) { + for (int sum = half_sum; sum >= num; --sum) { + possible[sum] |= possible[sum - num]; + if (sum == half_sum && possible[half_sum]) { + return true; + } + } + } + + return false; + } +};