|
| 1 | +# 2023.10.23 残酷刷题 | 力扣 2518. 好分区的数目 |
| 2 | + |
| 3 | +## 题目 |
| 4 | + |
| 5 | +给你一个正整数数组 `nums` 和一个整数 `k` 。 |
| 6 | + |
| 7 | +**分区** 的定义是:将数组划分成两个有序的 **组** ,并满足每个元素 **恰好** 存在于 **某一个** 组中。如果分区中每个组的元素和都大于等于 `k` ,则认为分区是一个好分区。 |
| 8 | + |
| 9 | +返回 **不同** 的好分区的数目。由于答案可能很大,请返回对 `10<sup>9</sup> + 7` **取余** 后的结果。 |
| 10 | + |
| 11 | +如果在两个分区中,存在某个元素 `nums[i]` 被分在不同的组中,则认为这两个分区不同。 |
| 12 | + |
| 13 | +**示例 1:** |
| 14 | + |
| 15 | +**输入:**nums = \[1,2,3,4\], k = 4 |
| 16 | +**输出:**6 |
| 17 | +**解释:**好分区的情况是 (\[1,2,3\], \[4\]), (\[1,3\], \[2,4\]), (\[1,4\], \[2,3\]), (\[2,3\], \[1,4\]), (\[2,4\], \[1,3\]) 和 (\[4\], \[1,2,3\]) 。 |
| 18 | + |
| 19 | +**示例 2:** |
| 20 | + |
| 21 | +**输入:**nums = \[3,3,3\], k = 4 |
| 22 | +**输出:**0 |
| 23 | +**解释:**数组中不存在好分区。 |
| 24 | + |
| 25 | +**示例 3:** |
| 26 | + |
| 27 | +**输入:**nums = \[6,6\], k = 2 |
| 28 | +**输出:**2 |
| 29 | +**解释:**可以将 nums\[0\] 放入第一个分区或第二个分区中。 |
| 30 | +好分区的情况是 (\[6\], \[6\]) 和 (\[6\], \[6\]) 。 |
| 31 | + |
| 32 | +**提示:** |
| 33 | + |
| 34 | +- `1 <= nums.length, k <= 1000` |
| 35 | +- <code>1 <= nums[i] <= 10<sup>9</sup></code> |
| 36 | + |
| 37 | +## 算法——动态规划 |
| 38 | + |
| 39 | +- 算法类型:动态规划 |
| 40 | +- 算法解析 |
| 41 | + - 预计算 dp 数组 |
| 42 | + - `dp[i][l]` 表示前 `i` 个元素,构成和 `l` 的方案数 |
| 43 | + - `dp[0][0] = 1`、`dp[0][nums[0]] = 1` |
| 44 | + - `dp[i][l] = dp[i-1][l] + dp[i-1][l-nums[i]] (l>=nums[i])`,分为第 `i` 个数选择与不选择 2 种情况 |
| 45 | + - 最后数组分为两个区 A 和 B,需要 `sum(A) >= k` 且 `sum(B) >= k` |
| 46 | + - 需要 `sum(nums) >= 2k`,否则不会有答案,提前返回 |
| 47 | + - 总的划分方案数为 `2^n`,先减去 `sum(A) < k` 的方案数,其大小为 `t = dp[n-1][0] + dp[n-1][1] + ... + dp[n-1][k-1]` |
| 48 | + - 在 `sum(A) >= k` 的方案种,`sum(B) < k` 的方案数,其大小为 `t` |
| 49 | + - 因为 `sum(B) < k`,就能保证一定有 `sum(A) >= k`,所以就是 `sum(B) < k` 一个条件,和上面一样也是 t |
| 50 | + - 综上所述,答案就是 `2^n - 2t` |
| 51 | +- 算法复杂度 |
| 52 | + - 时间复杂度:`O(nk)` |
| 53 | + - 空间复杂度:`O(nk)` |
| 54 | + |
| 55 | +## JS 代码 |
| 56 | + |
| 57 | +```js |
| 58 | +/** |
| 59 | + * @param {number[]} nums |
| 60 | + * @param {number} k |
| 61 | + * @return {number} |
| 62 | + */ |
| 63 | +var countPartitions = function(nums, k) { |
| 64 | + const n = nums.length, s = nums.reduce((p, c) => p+c, 0) |
| 65 | + const dp = Array(n).fill(0).map(() => Array(k).fill(0)) |
| 66 | + dp[0][0] = 1, dp[0][nums[0]] = 1 |
| 67 | + let P = 1e9+7 |
| 68 | + // s>=2k |
| 69 | + if (s<2*k) return 0 |
| 70 | + for (let i = 1; i < n; i++) { |
| 71 | + for (let l = 0; l < k; l++) { |
| 72 | + // 前 i 个元素,构成和 l 的方案数 |
| 73 | + dp[i][l] = (dp[i-1][l] + ((l-nums[i]>=0) ? dp[i-1][l-nums[i]] : 0))%P |
| 74 | + } |
| 75 | + } |
| 76 | + let qpow = (a, b, p = P) => { |
| 77 | + let ans = 1%p |
| 78 | + while (b) { |
| 79 | + if (b&1) ans = Number(BigInt(ans)*BigInt(a)%BigInt(p)) |
| 80 | + a = Number(BigInt(a)*BigInt(a)%BigInt(p)) |
| 81 | + b>>=1 |
| 82 | + } |
| 83 | + return ans |
| 84 | + } |
| 85 | + let t = 0 |
| 86 | + for (let i = 0; i < k; i++) t += dp[n-1][i], t %= P |
| 87 | + return ((qpow(2, n) - t - t)%P+P)%P |
| 88 | +}; |
| 89 | +``` |
0 commit comments