|
| 1 | +""" |
| 2 | +Partition Equal Subset Sum |
| 3 | +
|
| 4 | +Given a list of positive integers, determine whether it can be partitioned |
| 5 | +into two subsets such that the sum of elements in both subsets is equal. |
| 6 | +
|
| 7 | +This is a classic dynamic-programming problem equivalent to asking: |
| 8 | +"Does any subset of the list sum to exactly half the total?" |
| 9 | +
|
| 10 | +If the total sum is odd, no equal partition exists. |
| 11 | +If the total sum is even, we run a 1-D DP (bitset / boolean array) to check |
| 12 | +whether the target value (total // 2) is reachable. |
| 13 | +
|
| 14 | +Time Complexity : O(n * total_sum) |
| 15 | +Space Complexity : O(total_sum) |
| 16 | +
|
| 17 | +References: |
| 18 | +- https://en.wikipedia.org/wiki/Partition_problem |
| 19 | +- https://leetcode.com/problems/partition-equal-subset-sum/ |
| 20 | +""" |
| 21 | + |
| 22 | + |
| 23 | +def partition_equal_subset_sum(nums: list[int]) -> bool: |
| 24 | + """ |
| 25 | + Return True if *nums* can be split into two subsets with equal sum, |
| 26 | + False otherwise. |
| 27 | +
|
| 28 | + Args: |
| 29 | + nums: A list of positive integers. |
| 30 | +
|
| 31 | + Returns: |
| 32 | + True if an equal-sum partition exists, False otherwise. |
| 33 | +
|
| 34 | + Raises: |
| 35 | + ValueError: If *nums* is empty or contains a non-positive integer. |
| 36 | +
|
| 37 | + Examples: |
| 38 | + >>> partition_equal_subset_sum([1, 5, 11, 5]) |
| 39 | + True |
| 40 | + >>> partition_equal_subset_sum([1, 2, 3, 5]) |
| 41 | + False |
| 42 | + >>> partition_equal_subset_sum([1, 1]) |
| 43 | + True |
| 44 | + >>> partition_equal_subset_sum([3, 3, 3, 4, 5]) |
| 45 | + True |
| 46 | + >>> partition_equal_subset_sum([2]) |
| 47 | + False |
| 48 | + >>> partition_equal_subset_sum([1, 2, 5]) |
| 49 | + False |
| 50 | + >>> partition_equal_subset_sum([100]) |
| 51 | + False |
| 52 | + """ |
| 53 | + if not nums: |
| 54 | + raise ValueError("nums must be a non-empty list.") |
| 55 | + if any(n <= 0 for n in nums): |
| 56 | + raise ValueError("All elements of nums must be positive integers.") |
| 57 | + |
| 58 | + total = sum(nums) |
| 59 | + |
| 60 | + # An odd total can never be split into two equal integer-sum subsets. |
| 61 | + if total % 2 != 0: |
| 62 | + return False |
| 63 | + |
| 64 | + target = total // 2 |
| 65 | + |
| 66 | + # dp[s] is True if sum s is achievable with some subset of nums seen so far. |
| 67 | + # We start knowing that the empty subset achieves sum 0. |
| 68 | + dp: list[bool] = [False] * (target + 1) |
| 69 | + dp[0] = True |
| 70 | + |
| 71 | + for num in nums: |
| 72 | + # Traverse right-to-left to avoid counting the same element twice |
| 73 | + # (0/1 knapsack style). |
| 74 | + for s in range(target, num - 1, -1): |
| 75 | + dp[s] = dp[s] or dp[s - num] |
| 76 | + |
| 77 | + if dp[target]: |
| 78 | + # Early exit: target already reachable. |
| 79 | + return True |
| 80 | + |
| 81 | + return dp[target] |
| 82 | + |
| 83 | + |
| 84 | +if __name__ == "__main__": |
| 85 | + import doctest |
| 86 | + |
| 87 | + doctest.testmod() |
| 88 | + |
| 89 | + print("Partition Equal Subset Sum — Demo") |
| 90 | + print("=" * 42) |
| 91 | + |
| 92 | + examples: list[tuple[list[int], bool]] = [ |
| 93 | + ([1, 5, 11, 5], True), |
| 94 | + ([1, 2, 3, 5], False), |
| 95 | + ([1, 1], True), |
| 96 | + ([3, 3, 3, 4, 5], True), |
| 97 | + ([2], False), |
| 98 | + ([1, 2, 5], False), |
| 99 | + ([2, 2, 1, 1], True), |
| 100 | + ([7, 3, 1, 5, 4], True), |
| 101 | + ] |
| 102 | + |
| 103 | + for nums, expected in examples: |
| 104 | + result = partition_equal_subset_sum(nums) |
| 105 | + status = "✓" if result == expected else "✗" |
| 106 | + print(f" {status} {nums!s:<25} → {result}") |
0 commit comments