diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md index b259dac9984f0..ba4e8344bd56c 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md @@ -56,13 +56,13 @@ tags: ### 方法一:DFS + 剪枝 -根据题意,我们需要将数组 `nums` 划分为 $k$ 个子集,且每个子集的和相等。因此,先累加 `nums` 中所有元素的和,如果不能被 $k$ 整除,说明无法划分为 $k$ 个子集,提前返回 `false`。 +根据题意,我们需要将数组 $\textit{nums}$ 划分为 $k$ 个子集,且每个子集的和相等。因此,先累加 $\textit{nums}$ 中所有元素的和,如果不能被 $k$ 整除,说明无法划分为 $k$ 个子集,提前返回 $\textit{false}$。 -如果能被 $k$ 整除,不妨将每个子集期望的和记为 $s$,然后创建一个长度为 $k$ 的数组 `cur`,表示当前每个子集的和。 +如果能被 $k$ 整除,不妨将每个子集期望的和记为 $s$,然后创建一个长度为 $k$ 的数组 $\textit{cur}$,表示当前每个子集的和。 -对数组 `nums` 进行降序排序(减少搜索次数),然后从第一个元素开始,依次尝试将其加入到 `cur` 的每个子集中。这里如果将 `nums[i]` 加入某个子集 `cur[j]` 后,子集的和超过 $s$,说明无法放入,可以直接跳过;另外,如果 `cur[j]` 与 `cur[j - 1]` 相等,意味着我们在 `cur[j - 1]` 的时候已经完成了搜索,也可以跳过当前的搜索。 +对数组 $\textit{nums}$ 进行降序排序(减少搜索次数),然后从第一个元素开始,依次尝试将其加入到 $\textit{cur}$ 的每个子集中。这里如果将 $\textit{nums}[i]$ 加入某个子集 $\textit{cur}[j]$ 后,子集的和超过 $s$,说明无法放入,可以直接跳过;另外,如果 $\textit{cur}[j]$ 与 $\textit{cur}[j - 1]$ 相等,意味着我们在 $\textit{cur}[j - 1]$ 的时候已经完成了搜索,也可以跳过当前的搜索。 -如果能将所有元素都加入到 `cur` 中,说明可以划分为 $k$ 个子集,返回 `true`。 +如果能将所有元素都加入到 $\textit{cur}$ 中,说明可以划分为 $k$ 个子集,返回 $\textit{true}$。 @@ -145,8 +145,7 @@ public: s /= k; int n = nums.size(); vector cur(k); - function dfs; - dfs = [&](int i) { + function dfs = [&](int i) { if (i == n) { return true; } @@ -210,31 +209,32 @@ func canPartitionKSubsets(nums []int, k int) bool { ```ts function canPartitionKSubsets(nums: number[], k: number): boolean { - let s = nums.reduce((a, b) => a + b); - if (s % k !== 0) { - return false; - } - s /= k; - nums.sort((a, b) => a - b); - const n = nums.length; - const f: boolean[] = new Array(1 << n).fill(false); - f[0] = true; - const cur: number[] = new Array(n).fill(0); - for (let i = 0; i < 1 << n; ++i) { - if (!f[i]) { - continue; + const dfs = (i: number): boolean => { + if (i === nums.length) { + return true; } - for (let j = 0; j < n; ++j) { - if (cur[i] + nums[j] > s) { - break; + for (let j = 0; j < k; j++) { + if (j > 0 && cur[j] === cur[j - 1]) { + continue; } - if (((i >> j) & 1) === 0) { - f[i | (1 << j)] = true; - cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + cur[j] += nums[i]; + if (cur[j] <= s && dfs(i + 1)) { + return true; } + cur[j] -= nums[i]; } + return false; + }; + + let s = nums.reduce((a, b) => a + b, 0); + const mod = s % k; + if (mod !== 0) { + return false; } - return f[(1 << n) - 1]; + s = Math.floor(s / k); + const cur = Array(k).fill(0); + nums.sort((a, b) => b - a); + return dfs(0); } ``` @@ -246,22 +246,22 @@ function canPartitionKSubsets(nums: number[], k: number): boolean { ### 方法二:状态压缩 + 记忆化搜索 -与方法一相同,我们依然先判断数组 `nums` 是否有可能被划分为 $k$ 个子集。如果不能被 $k$ 整除,直接返回 `false`。 +与方法一相同,我们依然先判断数组 $\textit{nums}$ 是否有可能被划分为 $k$ 个子集。如果不能被 $k$ 整除,直接返回 $\textit{false}$。 -我们记 $s$ 为每个子集期望的和,当前元素被划分的情况为 `state`。对于第 $i$ 个数,若 `((state >> i) & 1)` 等于 $0$,说明第 $i$ 个元素未被划分。 +我们记 $s$ 为每个子集期望的和,当前元素被划分的情况为 $\textit{state}$。对于第 $i$ 个数,若 $\textit{state}$ 的第 $i$ 位为 $0$,说明第 $i$ 个元素未被划分。 我们的目标是从全部元素中凑出 $k$ 个和为 $s$ 的子集。记当前子集的和为 $t$。在未划分第 $i$ 个元素时: -- 若 $t + nums[i] \gt s$,说明第 $i$ 个元素不能被添加到当前子集中,由于我们对 `nums` 数组进行升序排列,因此数组 `nums` 从位置 $i$ 开始的所有元素都不能被添加到当前子集,直接返回 `false`。 -- 否则,将第 $i$ 个元素添加到当前子集中,状态变为 `state | (1 << i)`,然后继续对未划分的元素进行搜索。需要注意的是,若 $t + nums[i] = s$,说明恰好可以得到一个和为 $s$ 的子集,下一步将 $t$ 归零(可以通过 `(t + nums[i]) % s` 实现),并继续划分下一个子集。 +- 若 $t + \textit{nums}[i] \gt s$,说明第 $i$ 个元素不能被添加到当前子集中,由于我们对 $\textit{nums}$ 数组进行升序排列,因此数组 $\textit{nums}$ 从位置 $i$ 开始的所有元素都不能被添加到当前子集,直接返回 $\textit{false}$。 +- 否则,将第 $i$ 个元素添加到当前子集中,状态变为 $\textit{state} | 2^i$,然后继续对未划分的元素进行搜索。需要注意的是,若 $t + \textit{nums}[i] = s$,说明恰好可以得到一个和为 $s$ 的子集,下一步将 $t$ 归零(可以通过 $(t + \textit{nums}[i]) \bmod s$ 实现),并继续划分下一个子集。 -为了避免重复搜索,我们使用一个长度为 $2^n$ 的数组 `f` 记录每个状态下的搜索结果。数组 `f` 有三个可能的值: +为了避免重复搜索,我们使用一个长度为 $2^n$ 的数组 $\textit{f}$ 记录每个状态下的搜索结果。数组 $\textit{f}$ 有三个可能的值: - `0`:表示当前状态还未搜索过; - `-1`:表示当前状态下无法划分为 $k$ 个子集; - `1`:表示当前状态下可以划分为 $k$ 个子集。 -时间复杂度 $O(n\times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $nums$ 的长度。对于每个状态,我们需要遍历数组 `nums`,时间复杂度为 $O(n)$;状态总数为 $2^n$,因此总的时间复杂度为 $O(n\times 2^n)$。 +时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $\textit{nums}$ 的长度。对于每个状态,我们需要遍历数组 $\textit{nums}$,时间复杂度为 $O(n)$;状态总数为 $2^n$,因此总的时间复杂度为 $O(n\times 2^n)$。 @@ -355,8 +355,7 @@ public: int n = nums.size(); int mask = (1 << n) - 1; vector f(1 << n); - function dfs; - dfs = [&](int state, int t) { + function dfs = [&](int state, int t) { if (state == mask) { return true; } @@ -428,6 +427,47 @@ func canPartitionKSubsets(nums []int, k int) bool { } ``` +#### TypeScript + +```ts +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b, 0); + if (s % k !== 0) { + return false; + } + s = Math.floor(s / k); + nums.sort((a, b) => a - b); + const n = nums.length; + const mask = (1 << n) - 1; + const f = Array(1 << n).fill(0); + + const dfs = (state: number, t: number): boolean => { + if (state === mask) { + return true; + } + if (f[state] !== 0) { + return f[state] === 1; + } + for (let i = 0; i < n; ++i) { + if ((state >> i) & 1) { + continue; + } + if (t + nums[i] > s) { + break; + } + if (dfs(state | (1 << i), (t + nums[i]) % s)) { + f[state] = 1; + return true; + } + } + f[state] = -1; + return false; + }; + + return dfs(0, 0); +} +``` + @@ -438,13 +478,13 @@ func canPartitionKSubsets(nums []int, k int) bool { 我们可以使用动态规划的方法求解本题。 -我们定义 $f[i]$ 表示当前选取的数字的状态为 $i$ 时,是否存在 $k$ 个子集满足题目要求。初始时 $f[0]=true$,答案为 $f[2^n-1]$。其中 $n$ 表示数组 $nums$ 的长度。另外,我们定义 $cur[i]$ 表示当前选取的数字的状态为 $i$ 时,最后一个子集的和。 +我们定义 $f[i]$ 表示当前选取的数字的状态为 $i$ 时,是否存在 $k$ 个子集满足题目要求。初始时 $f[0]= true$,答案为 $f[2^n-1]$。其中 $n$ 表示数组 $nums$ 的长度。另外,我们定义 $cur[i]$ 表示当前选取的数字的状态为 $i$ 时,最后一个子集的和。 -我们在 $[0,2^n)$ 的范围内枚举状态 $i$,对于每个状态 $i$,如果 $f[i]$ 为 `false`,我们直接跳过即可。否则,我们枚举 $nums$ 数组中的任意一个数 $nums[j]$,如果 $cur[i] + nums[j] \gt s$,我们直接跳出枚举循环,因为后面的数更大,无法放入当前子集;否则,如果 $i$ 的二进制表示的第 $j$ 位为 $0$,说明当前 $nums[j]$ 还没有被选取,我们可以将其放入当前子集中,此时状态变为 $i | 2^j$,并更新 $cur[i | 2^j] = (cur[i] + nums[j]) \bmod s$,并且 $f[i | 2^j] = true$。 +我们在 $[0, 2^n]$ 的范围内枚举状态 $i$,对于每个状态 $i$,如果 $f[i]$ 为 $\textit{false}$,我们直接跳过即可。否则,我们枚举 $\textit{nums}$ 数组中的任意一个数 $\textit{nums}[j]$,如果 $\textit{cur}[i] + \textit{nums}[j] > s$,我们直接跳出枚举循环,因为后面的数更大,无法放入当前子集;否则,如果 $i$ 的二进制表示的第 $j$ 位为 $0$,说明当前 $\textit{nums}[j]$ 还没有被选取,我们可以将其放入当前子集中,此时状态变为 $i | 2^j$,并更新 $\textit{cur}[i | 2^j] = (\textit{cur}[i] + \textit{nums}[j]) \bmod s$,并且 $f[i | 2^j] = \textit{true}$。 -最后,我们返回 $f[2^n-1]$ 即可。 +最后,我们返回 $f[2^n - 1]$ 即可。 -时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $nums$ 的长度。 +时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $\textit{nums}$ 的长度。 @@ -584,6 +624,38 @@ func canPartitionKSubsets(nums []int, k int) bool { } ``` +#### TypeScript + +```ts +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b); + if (s % k !== 0) { + return false; + } + s /= k; + nums.sort((a, b) => a - b); + const n = nums.length; + const f: boolean[] = Array(1 << n).fill(false); + f[0] = true; + const cur: number[] = Array(n).fill(0); + for (let i = 0; i < 1 << n; ++i) { + if (!f[i]) { + continue; + } + for (let j = 0; j < n; ++j) { + if (cur[i] + nums[j] > s) { + break; + } + if (((i >> j) & 1) === 0) { + f[i | (1 << j)] = true; + cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + } + } + } + return f[(1 << n) - 1]; +} +``` + diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md index 0904227f64a6a..7e8cd3303f64d 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md @@ -54,7 +54,15 @@ tags: -### Solution 1 +### Solution 1: DFS + Pruning + +According to the problem description, we need to partition the array $\textit{nums}$ into $k$ subsets such that the sum of each subset is equal. Therefore, we first sum all the elements in $\textit{nums}$. If the total sum cannot be divided by $k$, it means we cannot partition the array into $k$ subsets, and we return $\textit{false}$ early. + +If the total sum can be divided by $k$, let's denote the expected sum of each subset as $s$. Then, we create an array $\textit{cur}$ of length $k$ to represent the current sum of each subset. + +We sort the array $\textit{nums}$ in descending order (to reduce the number of searches), and then start from the first element, trying to add it to each subset in $\textit{cur}$ one by one. If adding $\textit{nums}[i]$ to a subset $\textit{cur}[j]$ makes the subset's sum exceed $s$, it means it cannot be placed in that subset, and we can skip it. Additionally, if $\textit{cur}[j]$ is equal to $\textit{cur}[j - 1]$, it means we have already completed the search for $\textit{cur}[j - 1]$, and we can skip the current search. + +If we can add all elements to $\textit{cur}$, it means we can partition the array into $k$ subsets, and we return $\textit{true}$. @@ -63,7 +71,7 @@ tags: ```python class Solution: def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: - def dfs(i): + def dfs(i: int) -> bool: if i == len(nums): return True for j in range(k): @@ -137,8 +145,7 @@ public: s /= k; int n = nums.size(); vector cur(k); - function dfs; - dfs = [&](int i) { + function dfs = [&](int i) { if (i == n) { return true; } @@ -202,31 +209,32 @@ func canPartitionKSubsets(nums []int, k int) bool { ```ts function canPartitionKSubsets(nums: number[], k: number): boolean { - let s = nums.reduce((a, b) => a + b); - if (s % k !== 0) { - return false; - } - s /= k; - nums.sort((a, b) => a - b); - const n = nums.length; - const f: boolean[] = new Array(1 << n).fill(false); - f[0] = true; - const cur: number[] = new Array(n).fill(0); - for (let i = 0; i < 1 << n; ++i) { - if (!f[i]) { - continue; + const dfs = (i: number): boolean => { + if (i === nums.length) { + return true; } - for (let j = 0; j < n; ++j) { - if (cur[i] + nums[j] > s) { - break; + for (let j = 0; j < k; j++) { + if (j > 0 && cur[j] === cur[j - 1]) { + continue; } - if (((i >> j) & 1) === 0) { - f[i | (1 << j)] = true; - cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + cur[j] += nums[i]; + if (cur[j] <= s && dfs(i + 1)) { + return true; } + cur[j] -= nums[i]; } + return false; + }; + + let s = nums.reduce((a, b) => a + b, 0); + const mod = s % k; + if (mod !== 0) { + return false; } - return f[(1 << n) - 1]; + s = Math.floor(s / k); + const cur = Array(k).fill(0); + nums.sort((a, b) => b - a); + return dfs(0); } ``` @@ -236,7 +244,24 @@ function canPartitionKSubsets(nums: number[], k: number): boolean { -### Solution 2 +### Solution 2: State Compression + Memoization + +Similar to Solution 1, we first check whether the array $\textit{nums}$ can be partitioned into $k$ subsets. If it cannot be divided by $k$, we directly return $\textit{false}$. + +Let $s$ be the expected sum of each subset, and let $\textit{state}$ represent the current partitioning state of the elements. For the $i$-th number, if the $i$-th bit of $\textit{state}$ is $0$, it means the $i$-th element has not been partitioned. + +Our goal is to form $k$ subsets with a sum of $s$ from all elements. Let $t$ be the current sum of the subset. When the $i$-th element is not partitioned: + +- If $t + \textit{nums}[i] \gt s$, it means the $i$-th element cannot be added to the current subset. Since we sort the array $\textit{nums}$ in ascending order, all elements from position $i$ onwards cannot be added to the current subset, and we directly return $\textit{false}$. +- Otherwise, add the $i$-th element to the current subset, change the state to $\textit{state} | 2^i$, and continue searching for unpartitioned elements. Note that if $t + \textit{nums}[i] = s$, it means we can form a subset with a sum of $s$. The next step is to reset $t$ to zero (which can be achieved by $(t + \textit{nums}[i]) \bmod s$) and continue partitioning the next subset. + +To avoid repeated searches, we use an array $\textit{f}$ of length $2^n$ to record the search results for each state. The array $\textit{f}$ has three possible values: + +- `0` indicates that the current state has not been searched yet; +- `-1`: indicates that the current state cannot be partitioned into $k$ subsets; +- `1`: indicates that the current state can be partitioned into $k$ subsets. + +The time complexity is $O(n \times 2^n)$, and the space complexity is $O(2^n)$. Here, $n$ represents the length of the array $\textit{nums}$. For each state, we need to traverse the array $\textit{nums}$, which has a time complexity of $O(n)$. The total number of states is $2^n$, so the overall time complexity is $O(n \times 2^n)$. @@ -330,8 +355,7 @@ public: int n = nums.size(); int mask = (1 << n) - 1; vector f(1 << n); - function dfs; - dfs = [&](int state, int t) { + function dfs = [&](int state, int t) { if (state == mask) { return true; } @@ -403,13 +427,64 @@ func canPartitionKSubsets(nums []int, k int) bool { } ``` +#### TypeScript + +```ts +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b, 0); + if (s % k !== 0) { + return false; + } + s = Math.floor(s / k); + nums.sort((a, b) => a - b); + const n = nums.length; + const mask = (1 << n) - 1; + const f = Array(1 << n).fill(0); + + const dfs = (state: number, t: number): boolean => { + if (state === mask) { + return true; + } + if (f[state] !== 0) { + return f[state] === 1; + } + for (let i = 0; i < n; ++i) { + if ((state >> i) & 1) { + continue; + } + if (t + nums[i] > s) { + break; + } + if (dfs(state | (1 << i), (t + nums[i]) % s)) { + f[state] = 1; + return true; + } + } + f[state] = -1; + return false; + }; + + return dfs(0, 0); +} +``` + -### Solution 3 +### Solution 3: Dynamic Programming + +We can use dynamic programming to solve this problem. + +We define $f[i]$ to represent whether there exists $k$ subsets that meet the requirements when the current selected numbers' state is $i$. Initially, $f[0] = \text{true}$, and the answer is $f[2^n - 1]$, where $n$ is the length of the array $\textit{nums}$. Additionally, we define $cur[i]$ to represent the sum of the last subset when the current selected numbers' state is $i$. + +We enumerate the states $i$ in the range $[0, 2^n]$. For each state $i$, if $f[i]$ is $\text{false}$, we skip it. Otherwise, we enumerate any number $\textit{nums}[j]$ in the array $\textit{nums}$. If $cur[i] + \textit{nums}[j] > s$, we break the enumeration loop because the subsequent numbers are larger and cannot be placed in the current subset. Otherwise, if the $j$-th bit of the binary representation of $i$ is $0$, it means the current $\textit{nums}[j]$ has not been selected. We can place it in the current subset, change the state to $i | 2^j$, update $cur[i | 2^j] = (cur[i] + \textit{nums}[j]) \bmod s$, and set $f[i | 2^j] = \text{true}$. + +Finally, we return $f[2^n - 1]$. + +The time complexity is $O(n \times 2^n)$, and the space complexity is $O(2^n)$. Here, $n$ represents the length of the array $\textit{nums}$. @@ -549,6 +624,38 @@ func canPartitionKSubsets(nums []int, k int) bool { } ``` +#### TypeScript + +```ts +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b); + if (s % k !== 0) { + return false; + } + s /= k; + nums.sort((a, b) => a - b); + const n = nums.length; + const f: boolean[] = Array(1 << n).fill(false); + f[0] = true; + const cur: number[] = Array(n).fill(0); + for (let i = 0; i < 1 << n; ++i) { + if (!f[i]) { + continue; + } + for (let j = 0; j < n; ++j) { + if (cur[i] + nums[j] > s) { + break; + } + if (((i >> j) & 1) === 0) { + f[i | (1 << j)] = true; + cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + } + } + } + return f[(1 << n) - 1]; +} +``` + diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.cpp b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.cpp index 4624b6ac3e12c..07140a6d0f88c 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.cpp +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.cpp @@ -8,8 +8,7 @@ class Solution { s /= k; int n = nums.size(); vector cur(k); - function dfs; - dfs = [&](int i) { + function dfs = [&](int i) { if (i == n) { return true; } @@ -28,4 +27,4 @@ class Solution { sort(nums.begin(), nums.end(), greater()); return dfs(0); } -}; \ No newline at end of file +}; diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.py b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.py index 28bb91b8043da..a408312d49ee9 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.py +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.py @@ -1,6 +1,6 @@ class Solution: def canPartitionKSubsets(self, nums: List[int], k: int) -> bool: - def dfs(i): + def dfs(i: int) -> bool: if i == len(nums): return True for j in range(k): diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.ts b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.ts index 69d688c6dbc92..236fc2b85c0e4 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.ts +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.ts @@ -1,27 +1,28 @@ function canPartitionKSubsets(nums: number[], k: number): boolean { - let s = nums.reduce((a, b) => a + b); - if (s % k !== 0) { - return false; - } - s /= k; - nums.sort((a, b) => a - b); - const n = nums.length; - const f: boolean[] = new Array(1 << n).fill(false); - f[0] = true; - const cur: number[] = new Array(n).fill(0); - for (let i = 0; i < 1 << n; ++i) { - if (!f[i]) { - continue; + const dfs = (i: number): boolean => { + if (i === nums.length) { + return true; } - for (let j = 0; j < n; ++j) { - if (cur[i] + nums[j] > s) { - break; + for (let j = 0; j < k; j++) { + if (j > 0 && cur[j] === cur[j - 1]) { + continue; } - if (((i >> j) & 1) === 0) { - f[i | (1 << j)] = true; - cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + cur[j] += nums[i]; + if (cur[j] <= s && dfs(i + 1)) { + return true; } + cur[j] -= nums[i]; } + return false; + }; + + let s = nums.reduce((a, b) => a + b, 0); + const mod = s % k; + if (mod !== 0) { + return false; } - return f[(1 << n) - 1]; + s = Math.floor(s / k); + const cur = Array(k).fill(0); + nums.sort((a, b) => b - a); + return dfs(0); } diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.cpp b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.cpp index 27aa6624efacc..706d16657cee2 100644 --- a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.cpp +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.cpp @@ -10,8 +10,7 @@ class Solution { int n = nums.size(); int mask = (1 << n) - 1; vector f(1 << n); - function dfs; - dfs = [&](int state, int t) { + function dfs = [&](int state, int t) { if (state == mask) { return true; } @@ -35,4 +34,4 @@ class Solution { }; return dfs(0, 0); } -}; \ No newline at end of file +}; diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.ts b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.ts new file mode 100644 index 0000000000000..c9a183d059095 --- /dev/null +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.ts @@ -0,0 +1,36 @@ +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b, 0); + if (s % k !== 0) { + return false; + } + s = Math.floor(s / k); + nums.sort((a, b) => a - b); + const n = nums.length; + const mask = (1 << n) - 1; + const f = Array(1 << n).fill(0); + + const dfs = (state: number, t: number): boolean => { + if (state === mask) { + return true; + } + if (f[state] !== 0) { + return f[state] === 1; + } + for (let i = 0; i < n; ++i) { + if ((state >> i) & 1) { + continue; + } + if (t + nums[i] > s) { + break; + } + if (dfs(state | (1 << i), (t + nums[i]) % s)) { + f[state] = 1; + return true; + } + } + f[state] = -1; + return false; + }; + + return dfs(0, 0); +} diff --git a/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution3.ts b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution3.ts new file mode 100644 index 0000000000000..2d1d28df96bc8 --- /dev/null +++ b/solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution3.ts @@ -0,0 +1,27 @@ +function canPartitionKSubsets(nums: number[], k: number): boolean { + let s = nums.reduce((a, b) => a + b); + if (s % k !== 0) { + return false; + } + s /= k; + nums.sort((a, b) => a - b); + const n = nums.length; + const f: boolean[] = Array(1 << n).fill(false); + f[0] = true; + const cur: number[] = Array(n).fill(0); + for (let i = 0; i < 1 << n; ++i) { + if (!f[i]) { + continue; + } + for (let j = 0; j < n; ++j) { + if (cur[i] + nums[j] > s) { + break; + } + if (((i >> j) & 1) === 0) { + f[i | (1 << j)] = true; + cur[i | (1 << j)] = (cur[i] + nums[j]) % s; + } + } + } + return f[(1 << n) - 1]; +}