diff --git a/leetcode/416/memo.md b/leetcode/416/memo.md new file mode 100644 index 0000000..a346434 --- /dev/null +++ b/leetcode/416/memo.md @@ -0,0 +1,34 @@ +# Step 1 + +合計値が `sum(nums) // 2` と一致する組み合わせを見つければいい、と考えた。 +素直にやるなら、各 index においてその要素を使うか使わないかで分岐していって、subsetの合計値をチェックしていけばいいのだが... + +> `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 に分類されるらしい。 + +# 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/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] 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 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]; + } +}; 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]; + } +}; 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; + } +};