Skip to content

Commit f7895bb

Browse files
authoredAug 25, 2024
feat: add solutions to lc problem: No.0698 (doocs#3447)
No.0698.Partition to K Equal Sum Subsets
1 parent 3e47705 commit f7895bb

File tree

8 files changed

+334
-93
lines changed

8 files changed

+334
-93
lines changed
 

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/README.md

Lines changed: 110 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,13 @@ tags:
5656

5757
### 方法一:DFS + 剪枝
5858

59-
根据题意,我们需要将数组 `nums` 划分为 $k$ 个子集,且每个子集的和相等。因此,先累加 `nums` 中所有元素的和,如果不能被 $k$ 整除,说明无法划分为 $k$ 个子集,提前返回 `false`
59+
根据题意,我们需要将数组 $\textit{nums}$ 划分为 $k$ 个子集,且每个子集的和相等。因此,先累加 $\textit{nums}$ 中所有元素的和,如果不能被 $k$ 整除,说明无法划分为 $k$ 个子集,提前返回 $\textit{false}$
6060

61-
如果能被 $k$ 整除,不妨将每个子集期望的和记为 $s$,然后创建一个长度为 $k$ 的数组 `cur`,表示当前每个子集的和。
61+
如果能被 $k$ 整除,不妨将每个子集期望的和记为 $s$,然后创建一个长度为 $k$ 的数组 $\textit{cur}$,表示当前每个子集的和。
6262

63-
对数组 `nums` 进行降序排序(减少搜索次数),然后从第一个元素开始,依次尝试将其加入到 `cur` 的每个子集中。这里如果将 `nums[i]` 加入某个子集 `cur[j]` 后,子集的和超过 $s$,说明无法放入,可以直接跳过;另外,如果 `cur[j]``cur[j - 1]` 相等,意味着我们在 `cur[j - 1]` 的时候已经完成了搜索,也可以跳过当前的搜索。
63+
对数组 $\textit{nums}$ 进行降序排序(减少搜索次数),然后从第一个元素开始,依次尝试将其加入到 $\textit{cur}$ 的每个子集中。这里如果将 $\textit{nums}[i]$ 加入某个子集 $\textit{cur}[j]$ 后,子集的和超过 $s$,说明无法放入,可以直接跳过;另外,如果 $\textit{cur}[j]$$\textit{cur}[j - 1]$ 相等,意味着我们在 $\textit{cur}[j - 1]$ 的时候已经完成了搜索,也可以跳过当前的搜索。
6464

65-
如果能将所有元素都加入到 `cur` 中,说明可以划分为 $k$ 个子集,返回 `true`
65+
如果能将所有元素都加入到 $\textit{cur}$ 中,说明可以划分为 $k$ 个子集,返回 $\textit{true}$
6666

6767
<!-- tabs:start -->
6868

@@ -145,8 +145,7 @@ public:
145145
s /= k;
146146
int n = nums.size();
147147
vector<int> cur(k);
148-
function<bool(int)> dfs;
149-
dfs = [&](int i) {
148+
function<bool(int)> dfs = [&](int i) {
150149
if (i == n) {
151150
return true;
152151
}
@@ -210,31 +209,32 @@ func canPartitionKSubsets(nums []int, k int) bool {
210209

211210
```ts
212211
function canPartitionKSubsets(nums: number[], k: number): boolean {
213-
let s = nums.reduce((a, b) => a + b);
214-
if (s % k !== 0) {
215-
return false;
216-
}
217-
s /= k;
218-
nums.sort((a, b) => a - b);
219-
const n = nums.length;
220-
const f: boolean[] = new Array(1 << n).fill(false);
221-
f[0] = true;
222-
const cur: number[] = new Array(n).fill(0);
223-
for (let i = 0; i < 1 << n; ++i) {
224-
if (!f[i]) {
225-
continue;
212+
const dfs = (i: number): boolean => {
213+
if (i === nums.length) {
214+
return true;
226215
}
227-
for (let j = 0; j < n; ++j) {
228-
if (cur[i] + nums[j] > s) {
229-
break;
216+
for (let j = 0; j < k; j++) {
217+
if (j > 0 && cur[j] === cur[j - 1]) {
218+
continue;
230219
}
231-
if (((i >> j) & 1) === 0) {
232-
f[i | (1 << j)] = true;
233-
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
220+
cur[j] += nums[i];
221+
if (cur[j] <= s && dfs(i + 1)) {
222+
return true;
234223
}
224+
cur[j] -= nums[i];
235225
}
226+
return false;
227+
};
228+
229+
let s = nums.reduce((a, b) => a + b, 0);
230+
const mod = s % k;
231+
if (mod !== 0) {
232+
return false;
236233
}
237-
return f[(1 << n) - 1];
234+
s = Math.floor(s / k);
235+
const cur = Array(k).fill(0);
236+
nums.sort((a, b) => b - a);
237+
return dfs(0);
238238
}
239239
```
240240

@@ -246,22 +246,22 @@ function canPartitionKSubsets(nums: number[], k: number): boolean {
246246

247247
### 方法二:状态压缩 + 记忆化搜索
248248

249-
与方法一相同,我们依然先判断数组 `nums` 是否有可能被划分为 $k$ 个子集。如果不能被 $k$ 整除,直接返回 `false`
249+
与方法一相同,我们依然先判断数组 $\textit{nums}$ 是否有可能被划分为 $k$ 个子集。如果不能被 $k$ 整除,直接返回 $\textit{false}$
250250

251-
我们记 $s$ 为每个子集期望的和,当前元素被划分的情况为 `state`。对于第 $i$ 个数,若 `((state >> i) & 1)` 等于 $0$,说明第 $i$ 个元素未被划分。
251+
我们记 $s$ 为每个子集期望的和,当前元素被划分的情况为 $\textit{state}$。对于第 $i$ 个数,若 $\textit{state}$ 的第 $i$ 位为 $0$,说明第 $i$ 个元素未被划分。
252252

253253
我们的目标是从全部元素中凑出 $k$ 个和为 $s$ 的子集。记当前子集的和为 $t$。在未划分第 $i$ 个元素时:
254254

255-
- 若 $t + nums[i] \gt s$,说明第 $i$ 个元素不能被添加到当前子集中,由于我们对 `nums` 数组进行升序排列,因此数组 `nums` 从位置 $i$ 开始的所有元素都不能被添加到当前子集,直接返回 `false`
256-
- 否则,将第 $i$ 个元素添加到当前子集中,状态变为 `state | (1 << i)`,然后继续对未划分的元素进行搜索。需要注意的是,若 $t + nums[i] = s$,说明恰好可以得到一个和为 $s$ 的子集,下一步将 $t$ 归零(可以通过 `(t + nums[i]) % s` 实现),并继续划分下一个子集。
255+
- 若 $t + \textit{nums}[i] \gt s$,说明第 $i$ 个元素不能被添加到当前子集中,由于我们对 $\textit{nums}$ 数组进行升序排列,因此数组 $\textit{nums}$ 从位置 $i$ 开始的所有元素都不能被添加到当前子集,直接返回 $\textit{false}$
256+
- 否则,将第 $i$ 个元素添加到当前子集中,状态变为 $\textit{state} | 2^i$,然后继续对未划分的元素进行搜索。需要注意的是,若 $t + \textit{nums}[i] = s$,说明恰好可以得到一个和为 $s$ 的子集,下一步将 $t$ 归零(可以通过 $(t + \textit{nums}[i]) \bmod s$ 实现),并继续划分下一个子集。
257257

258-
为了避免重复搜索,我们使用一个长度为 $2^n$ 的数组 `f` 记录每个状态下的搜索结果。数组 `f` 有三个可能的值:
258+
为了避免重复搜索,我们使用一个长度为 $2^n$ 的数组 $\textit{f}$ 记录每个状态下的搜索结果。数组 $\textit{f}$ 有三个可能的值:
259259

260260
- `0`:表示当前状态还未搜索过;
261261
- `-1`:表示当前状态下无法划分为 $k$ 个子集;
262262
- `1`:表示当前状态下可以划分为 $k$ 个子集。
263263

264-
时间复杂度 $O(n\times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $nums$ 的长度。对于每个状态,我们需要遍历数组 `nums`,时间复杂度为 $O(n)$;状态总数为 $2^n$,因此总的时间复杂度为 $O(n\times 2^n)$。
264+
时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $\textit{nums}$ 的长度。对于每个状态,我们需要遍历数组 $\textit{nums}$,时间复杂度为 $O(n)$;状态总数为 $2^n$,因此总的时间复杂度为 $O(n\times 2^n)$。
265265

266266
<!-- tabs:start -->
267267

@@ -355,8 +355,7 @@ public:
355355
int n = nums.size();
356356
int mask = (1 << n) - 1;
357357
vector<int> f(1 << n);
358-
function<bool(int, int)> dfs;
359-
dfs = [&](int state, int t) {
358+
function<bool(int, int)> dfs = [&](int state, int t) {
360359
if (state == mask) {
361360
return true;
362361
}
@@ -428,6 +427,47 @@ func canPartitionKSubsets(nums []int, k int) bool {
428427
}
429428
```
430429

430+
#### TypeScript
431+
432+
```ts
433+
function canPartitionKSubsets(nums: number[], k: number): boolean {
434+
let s = nums.reduce((a, b) => a + b, 0);
435+
if (s % k !== 0) {
436+
return false;
437+
}
438+
s = Math.floor(s / k);
439+
nums.sort((a, b) => a - b);
440+
const n = nums.length;
441+
const mask = (1 << n) - 1;
442+
const f = Array(1 << n).fill(0);
443+
444+
const dfs = (state: number, t: number): boolean => {
445+
if (state === mask) {
446+
return true;
447+
}
448+
if (f[state] !== 0) {
449+
return f[state] === 1;
450+
}
451+
for (let i = 0; i < n; ++i) {
452+
if ((state >> i) & 1) {
453+
continue;
454+
}
455+
if (t + nums[i] > s) {
456+
break;
457+
}
458+
if (dfs(state | (1 << i), (t + nums[i]) % s)) {
459+
f[state] = 1;
460+
return true;
461+
}
462+
}
463+
f[state] = -1;
464+
return false;
465+
};
466+
467+
return dfs(0, 0);
468+
}
469+
```
470+
431471
<!-- tabs:end -->
432472

433473
<!-- solution:end -->
@@ -438,13 +478,13 @@ func canPartitionKSubsets(nums []int, k int) bool {
438478

439479
我们可以使用动态规划的方法求解本题。
440480

441-
我们定义 $f[i]$ 表示当前选取的数字的状态为 $i$ 时,是否存在 $k$ 个子集满足题目要求。初始时 $f[0]=true$,答案为 $f[2^n-1]$。其中 $n$ 表示数组 $nums$ 的长度。另外,我们定义 $cur[i]$ 表示当前选取的数字的状态为 $i$ 时,最后一个子集的和。
481+
我们定义 $f[i]$ 表示当前选取的数字的状态为 $i$ 时,是否存在 $k$ 个子集满足题目要求。初始时 $f[0]= true$,答案为 $f[2^n-1]$。其中 $n$ 表示数组 $nums$ 的长度。另外,我们定义 $cur[i]$ 表示当前选取的数字的状态为 $i$ 时,最后一个子集的和。
442482

443-
我们在 $[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$。
483+
我们在 $[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}$。
444484

445-
最后,我们返回 $f[2^n-1]$ 即可。
485+
最后,我们返回 $f[2^n - 1]$ 即可。
446486

447-
时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $nums$ 的长度。
487+
时间复杂度 $O(n \times 2^n)$,空间复杂度 $O(2^n)$。其中 $n$ 表示数组 $\textit{nums}$ 的长度。
448488

449489
<!-- tabs:start -->
450490

@@ -584,6 +624,38 @@ func canPartitionKSubsets(nums []int, k int) bool {
584624
}
585625
```
586626

627+
#### TypeScript
628+
629+
```ts
630+
function canPartitionKSubsets(nums: number[], k: number): boolean {
631+
let s = nums.reduce((a, b) => a + b);
632+
if (s % k !== 0) {
633+
return false;
634+
}
635+
s /= k;
636+
nums.sort((a, b) => a - b);
637+
const n = nums.length;
638+
const f: boolean[] = Array(1 << n).fill(false);
639+
f[0] = true;
640+
const cur: number[] = Array(n).fill(0);
641+
for (let i = 0; i < 1 << n; ++i) {
642+
if (!f[i]) {
643+
continue;
644+
}
645+
for (let j = 0; j < n; ++j) {
646+
if (cur[i] + nums[j] > s) {
647+
break;
648+
}
649+
if (((i >> j) & 1) === 0) {
650+
f[i | (1 << j)] = true;
651+
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
652+
}
653+
}
654+
}
655+
return f[(1 << n) - 1];
656+
}
657+
```
658+
587659
<!-- tabs:end -->
588660

589661
<!-- solution:end -->

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/README_EN.md

Lines changed: 135 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,15 @@ tags:
5454

5555
<!-- solution:start -->
5656

57-
### Solution 1
57+
### Solution 1: DFS + Pruning
58+
59+
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.
60+
61+
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.
62+
63+
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.
64+
65+
If we can add all elements to $\textit{cur}$, it means we can partition the array into $k$ subsets, and we return $\textit{true}$.
5866

5967
<!-- tabs:start -->
6068

@@ -63,7 +71,7 @@ tags:
6371
```python
6472
class Solution:
6573
def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
66-
def dfs(i):
74+
def dfs(i: int) -> bool:
6775
if i == len(nums):
6876
return True
6977
for j in range(k):
@@ -137,8 +145,7 @@ public:
137145
s /= k;
138146
int n = nums.size();
139147
vector<int> cur(k);
140-
function<bool(int)> dfs;
141-
dfs = [&](int i) {
148+
function<bool(int)> dfs = [&](int i) {
142149
if (i == n) {
143150
return true;
144151
}
@@ -202,31 +209,32 @@ func canPartitionKSubsets(nums []int, k int) bool {
202209

203210
```ts
204211
function canPartitionKSubsets(nums: number[], k: number): boolean {
205-
let s = nums.reduce((a, b) => a + b);
206-
if (s % k !== 0) {
207-
return false;
208-
}
209-
s /= k;
210-
nums.sort((a, b) => a - b);
211-
const n = nums.length;
212-
const f: boolean[] = new Array(1 << n).fill(false);
213-
f[0] = true;
214-
const cur: number[] = new Array(n).fill(0);
215-
for (let i = 0; i < 1 << n; ++i) {
216-
if (!f[i]) {
217-
continue;
212+
const dfs = (i: number): boolean => {
213+
if (i === nums.length) {
214+
return true;
218215
}
219-
for (let j = 0; j < n; ++j) {
220-
if (cur[i] + nums[j] > s) {
221-
break;
216+
for (let j = 0; j < k; j++) {
217+
if (j > 0 && cur[j] === cur[j - 1]) {
218+
continue;
222219
}
223-
if (((i >> j) & 1) === 0) {
224-
f[i | (1 << j)] = true;
225-
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
220+
cur[j] += nums[i];
221+
if (cur[j] <= s && dfs(i + 1)) {
222+
return true;
226223
}
224+
cur[j] -= nums[i];
227225
}
226+
return false;
227+
};
228+
229+
let s = nums.reduce((a, b) => a + b, 0);
230+
const mod = s % k;
231+
if (mod !== 0) {
232+
return false;
228233
}
229-
return f[(1 << n) - 1];
234+
s = Math.floor(s / k);
235+
const cur = Array(k).fill(0);
236+
nums.sort((a, b) => b - a);
237+
return dfs(0);
230238
}
231239
```
232240

@@ -236,7 +244,24 @@ function canPartitionKSubsets(nums: number[], k: number): boolean {
236244

237245
<!-- solution:start -->
238246

239-
### Solution 2
247+
### Solution 2: State Compression + Memoization
248+
249+
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}$.
250+
251+
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.
252+
253+
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:
254+
255+
- 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}$.
256+
- 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.
257+
258+
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:
259+
260+
- `0` indicates that the current state has not been searched yet;
261+
- `-1`: indicates that the current state cannot be partitioned into $k$ subsets;
262+
- `1`: indicates that the current state can be partitioned into $k$ subsets.
263+
264+
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)$.
240265

241266
<!-- tabs:start -->
242267

@@ -330,8 +355,7 @@ public:
330355
int n = nums.size();
331356
int mask = (1 << n) - 1;
332357
vector<int> f(1 << n);
333-
function<bool(int, int)> dfs;
334-
dfs = [&](int state, int t) {
358+
function<bool(int, int)> dfs = [&](int state, int t) {
335359
if (state == mask) {
336360
return true;
337361
}
@@ -403,13 +427,64 @@ func canPartitionKSubsets(nums []int, k int) bool {
403427
}
404428
```
405429

430+
#### TypeScript
431+
432+
```ts
433+
function canPartitionKSubsets(nums: number[], k: number): boolean {
434+
let s = nums.reduce((a, b) => a + b, 0);
435+
if (s % k !== 0) {
436+
return false;
437+
}
438+
s = Math.floor(s / k);
439+
nums.sort((a, b) => a - b);
440+
const n = nums.length;
441+
const mask = (1 << n) - 1;
442+
const f = Array(1 << n).fill(0);
443+
444+
const dfs = (state: number, t: number): boolean => {
445+
if (state === mask) {
446+
return true;
447+
}
448+
if (f[state] !== 0) {
449+
return f[state] === 1;
450+
}
451+
for (let i = 0; i < n; ++i) {
452+
if ((state >> i) & 1) {
453+
continue;
454+
}
455+
if (t + nums[i] > s) {
456+
break;
457+
}
458+
if (dfs(state | (1 << i), (t + nums[i]) % s)) {
459+
f[state] = 1;
460+
return true;
461+
}
462+
}
463+
f[state] = -1;
464+
return false;
465+
};
466+
467+
return dfs(0, 0);
468+
}
469+
```
470+
406471
<!-- tabs:end -->
407472

408473
<!-- solution:end -->
409474

410475
<!-- solution:start -->
411476

412-
### Solution 3
477+
### Solution 3: Dynamic Programming
478+
479+
We can use dynamic programming to solve this problem.
480+
481+
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$.
482+
483+
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}$.
484+
485+
Finally, we return $f[2^n - 1]$.
486+
487+
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}$.
413488

414489
<!-- tabs:start -->
415490

@@ -549,6 +624,38 @@ func canPartitionKSubsets(nums []int, k int) bool {
549624
}
550625
```
551626

627+
#### TypeScript
628+
629+
```ts
630+
function canPartitionKSubsets(nums: number[], k: number): boolean {
631+
let s = nums.reduce((a, b) => a + b);
632+
if (s % k !== 0) {
633+
return false;
634+
}
635+
s /= k;
636+
nums.sort((a, b) => a - b);
637+
const n = nums.length;
638+
const f: boolean[] = Array(1 << n).fill(false);
639+
f[0] = true;
640+
const cur: number[] = Array(n).fill(0);
641+
for (let i = 0; i < 1 << n; ++i) {
642+
if (!f[i]) {
643+
continue;
644+
}
645+
for (let j = 0; j < n; ++j) {
646+
if (cur[i] + nums[j] > s) {
647+
break;
648+
}
649+
if (((i >> j) & 1) === 0) {
650+
f[i | (1 << j)] = true;
651+
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
652+
}
653+
}
654+
}
655+
return f[(1 << n) - 1];
656+
}
657+
```
658+
552659
<!-- tabs:end -->
553660

554661
<!-- solution:end -->

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ class Solution {
88
s /= k;
99
int n = nums.size();
1010
vector<int> cur(k);
11-
function<bool(int)> dfs;
12-
dfs = [&](int i) {
11+
function<bool(int)> dfs = [&](int i) {
1312
if (i == n) {
1413
return true;
1514
}
@@ -28,4 +27,4 @@ class Solution {
2827
sort(nums.begin(), nums.end(), greater<int>());
2928
return dfs(0);
3029
}
31-
};
30+
};

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class Solution:
22
def canPartitionKSubsets(self, nums: List[int], k: int) -> bool:
3-
def dfs(i):
3+
def dfs(i: int) -> bool:
44
if i == len(nums):
55
return True
66
for j in range(k):
Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,28 @@
11
function canPartitionKSubsets(nums: number[], k: number): boolean {
2-
let s = nums.reduce((a, b) => a + b);
3-
if (s % k !== 0) {
4-
return false;
5-
}
6-
s /= k;
7-
nums.sort((a, b) => a - b);
8-
const n = nums.length;
9-
const f: boolean[] = new Array(1 << n).fill(false);
10-
f[0] = true;
11-
const cur: number[] = new Array(n).fill(0);
12-
for (let i = 0; i < 1 << n; ++i) {
13-
if (!f[i]) {
14-
continue;
2+
const dfs = (i: number): boolean => {
3+
if (i === nums.length) {
4+
return true;
155
}
16-
for (let j = 0; j < n; ++j) {
17-
if (cur[i] + nums[j] > s) {
18-
break;
6+
for (let j = 0; j < k; j++) {
7+
if (j > 0 && cur[j] === cur[j - 1]) {
8+
continue;
199
}
20-
if (((i >> j) & 1) === 0) {
21-
f[i | (1 << j)] = true;
22-
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
10+
cur[j] += nums[i];
11+
if (cur[j] <= s && dfs(i + 1)) {
12+
return true;
2313
}
14+
cur[j] -= nums[i];
2415
}
16+
return false;
17+
};
18+
19+
let s = nums.reduce((a, b) => a + b, 0);
20+
const mod = s % k;
21+
if (mod !== 0) {
22+
return false;
2523
}
26-
return f[(1 << n) - 1];
24+
s = Math.floor(s / k);
25+
const cur = Array(k).fill(0);
26+
nums.sort((a, b) => b - a);
27+
return dfs(0);
2728
}

‎solution/0600-0699/0698.Partition to K Equal Sum Subsets/Solution2.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ class Solution {
1010
int n = nums.size();
1111
int mask = (1 << n) - 1;
1212
vector<int> f(1 << n);
13-
function<bool(int, int)> dfs;
14-
dfs = [&](int state, int t) {
13+
function<bool(int, int)> dfs = [&](int state, int t) {
1514
if (state == mask) {
1615
return true;
1716
}
@@ -35,4 +34,4 @@ class Solution {
3534
};
3635
return dfs(0, 0);
3736
}
38-
};
37+
};
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
function canPartitionKSubsets(nums: number[], k: number): boolean {
2+
let s = nums.reduce((a, b) => a + b, 0);
3+
if (s % k !== 0) {
4+
return false;
5+
}
6+
s = Math.floor(s / k);
7+
nums.sort((a, b) => a - b);
8+
const n = nums.length;
9+
const mask = (1 << n) - 1;
10+
const f = Array(1 << n).fill(0);
11+
12+
const dfs = (state: number, t: number): boolean => {
13+
if (state === mask) {
14+
return true;
15+
}
16+
if (f[state] !== 0) {
17+
return f[state] === 1;
18+
}
19+
for (let i = 0; i < n; ++i) {
20+
if ((state >> i) & 1) {
21+
continue;
22+
}
23+
if (t + nums[i] > s) {
24+
break;
25+
}
26+
if (dfs(state | (1 << i), (t + nums[i]) % s)) {
27+
f[state] = 1;
28+
return true;
29+
}
30+
}
31+
f[state] = -1;
32+
return false;
33+
};
34+
35+
return dfs(0, 0);
36+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function canPartitionKSubsets(nums: number[], k: number): boolean {
2+
let s = nums.reduce((a, b) => a + b);
3+
if (s % k !== 0) {
4+
return false;
5+
}
6+
s /= k;
7+
nums.sort((a, b) => a - b);
8+
const n = nums.length;
9+
const f: boolean[] = Array(1 << n).fill(false);
10+
f[0] = true;
11+
const cur: number[] = Array(n).fill(0);
12+
for (let i = 0; i < 1 << n; ++i) {
13+
if (!f[i]) {
14+
continue;
15+
}
16+
for (let j = 0; j < n; ++j) {
17+
if (cur[i] + nums[j] > s) {
18+
break;
19+
}
20+
if (((i >> j) & 1) === 0) {
21+
f[i | (1 << j)] = true;
22+
cur[i | (1 << j)] = (cur[i] + nums[j]) % s;
23+
}
24+
}
25+
}
26+
return f[(1 << n) - 1];
27+
}

0 commit comments

Comments
 (0)
Please sign in to comment.