Skip to content

Commit 77029c8

Browse files
committed
feat: 每日一题更新
1 parent f955951 commit 77029c8

File tree

6 files changed

+256
-1
lines changed

6 files changed

+256
-1
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@
154154
"editor.tabSize": 2
155155
},
156156
"editor.codeActionsOnSave": {
157-
"source.fixAll.eslint": true
157+
"source.fixAll.eslint": "explicit"
158158
},
159159
"eslint.packageManager": "yarn",
160160
"typescript.tsdk": "node_modules\\typescript\\lib"
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @param {number[]} nums
3+
* @param {number} k
4+
* @return {number}
5+
*/
6+
var countPartitions = function(nums, k) {
7+
const n = nums.length, s = nums.reduce((p, c) => p+c, 0)
8+
const dp = Array(n).fill(0).map(() => Array(k).fill(0))
9+
dp[0][0] = 1, dp[0][nums[0]] = 1
10+
let P = 1e9+7
11+
// s>=2k
12+
if (s<2*k) return 0
13+
for (let i = 1; i < n; i++) {
14+
for (let l = 0; l < k; l++) {
15+
// 前 i 个元素,构成和 l 的方案数
16+
dp[i][l] = (dp[i-1][l] + ((l-nums[i]>=0) ? dp[i-1][l-nums[i]] : 0))%P
17+
}
18+
}
19+
let qpow = (a, b, p = P) => {
20+
let ans = 1%p
21+
while (b) {
22+
if (b&1) ans = Number(BigInt(ans)*BigInt(a)%BigInt(p))
23+
a = Number(BigInt(a)*BigInt(a)%BigInt(p))
24+
b>>=1
25+
}
26+
return ans
27+
}
28+
let t = 0
29+
for (let i = 0; i < k; i++) t += dp[n-1][i], t %= P
30+
return ((qpow(2, n) - t - t)%P+P)%P
31+
};
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @param {number} amount
3+
* @param {number[]} coins
4+
* @return {number}
5+
*/
6+
var change = function(amount, coins) {
7+
const n = coins.length, dp = Array(amount+1).fill(0)
8+
dp[0] = 1
9+
if (amount === 0) return 1
10+
for (let j = 0; j < n; j++) {
11+
for (let i = 1; i <= amount; i++) {
12+
if (i >= coins[j]) dp[i] += dp[i-coins[j]]
13+
}
14+
}
15+
return dp[amount]
16+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* @param {number} amount
3+
* @param {number[]} coins
4+
* @return {number}
5+
*/
6+
var change = function(amount, coins) {
7+
const n = coins.length, dp = Array(amount+1).fill(0).map(() => Array(n).fill(0))
8+
dp[0][0] = 1
9+
if (amount === 0) return 1
10+
for (let i = 1; i <= amount; i++) {
11+
let s = 0
12+
for (let j = 0; j < n; j++) {
13+
if (i >= coins[j]) {
14+
// 硬币递增,避免重复计算
15+
for (let k = 0; k <= j; k++) {
16+
dp[i][j] += dp[i-coins[j]][k]
17+
}
18+
}
19+
s += dp[i][j]
20+
}
21+
if (i === amount) return s
22+
}
23+
};
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# 残酷刷题 | LeetCode 518. Coin Change II
2+
3+
## 题目
4+
5+
给你一个整数数组 `coins` 表示不同面额的硬币,另给一个整数 `amount` 表示总金额。
6+
7+
请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 `0`
8+
9+
假设每一种面额的硬币有无限个。 
10+
11+
题目数据保证结果符合 32 位带符号整数。
12+
13+
**示例 1:**
14+
15+
**输入:**amount = 5, coins = \[1, 2, 5\]
16+
**输出:**4
17+
**解释:**有四种方式可以凑成总金额:
18+
5=5
19+
5=2+2+1
20+
5=2+1+1+1
21+
5=1+1+1+1+1
22+
23+
**示例 2:**
24+
25+
**输入:**amount = 3, coins = \[2\]
26+
**输出:**0
27+
**解释:**只用面额 2 的硬币不能凑成总金额 3 。
28+
29+
**示例 3:**
30+
31+
**输入:**amount = 10, coins = \[10\]
32+
**输出:**1
33+
34+
## 算法
35+
36+
- 算法类型:动态规划
37+
- 算法解析
38+
- `dp[i][j]` 表示组成数量 `i`,最后一个硬币是 `j` 的方案数,为了避免重复计算,我们要求硬币的组成顺序是不降序的
39+
- `dp[0][0] = 1`
40+
- `dp[i][j] = dp[i-coins[j]][k] (i>=coins[j], 2518. 好分区的数目
41+
k<=j)`
42+
- 算法复杂度
43+
- 时间复杂度:`O(amount*n^2)`
44+
- 空间复杂度:`O(amount*n)`
45+
- 可以继续优化:将 amount 和 coins 的循环调换,按照 coins 的顺序,可省去最内层的循环,时间复杂度为 `O(amount*n)`,空间复杂度为 `O(amount)`
46+
47+
## JS 代码
48+
49+
```js
50+
/**
51+
* @param {number} amount
52+
* @param {number[]} coins
53+
* @return {number}
54+
*/
55+
var change = function(amount, coins) {
56+
const n = coins.length, dp = Array(amount+1).fill(0).map(() => Array(n).fill(0))
57+
dp[0][0] = 1
58+
if (amount === 0) return 1
59+
for (let i = 1; i <= amount; i++) {
60+
let s = 0
61+
for (let j = 0; j < n; j++) {
62+
if (i >= coins[j]) {
63+
// 硬币递增,避免重复计算
64+
for (let k = 0; k <= j; k++) {
65+
dp[i][j] += dp[i-coins[j]][k]
66+
}
67+
}
68+
s += dp[i][j]
69+
}
70+
if (i === amount) return s
71+
}
72+
};
73+
74+
```
75+
76+
### 优化后
77+
78+
```js
79+
/**
80+
* @param {number} amount
81+
* @param {number[]} coins
82+
* @return {number}
83+
*/
84+
var change = function(amount, coins) {
85+
const n = coins.length, dp = Array(amount+1).fill(0)
86+
dp[0] = 1
87+
if (amount === 0) return 1
88+
for (let j = 0; j < n; j++) {
89+
for (let i = 1; i <= amount; i++) {
90+
if (i >= coins[j]) dp[i] += dp[i-coins[j]]
91+
}
92+
}
93+
return dp[amount]
94+
};
95+
96+
```

0 commit comments

Comments
 (0)