Skip to content

Commit f4e6a58

Browse files
committed
feat: 20260228 check in
Made-with: Cursor
1 parent 61d0b02 commit f4e6a58

4 files changed

Lines changed: 223 additions & 186 deletions

File tree

Lines changed: 45 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,65 @@
1-
# [123. 买卖股票的最佳时机 III](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description/?envType=study-plan-v2&envId=dynamic-programming)
1+
# [123. 买卖股票的最佳时机 III](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/description)
22

3-
> **作者**弘树
4-
> **日期**2024-09-21
5-
> **所用时间**13min
3+
> **日期**2024-09-21、2025-02-06
4+
> **所用时间**13min
5+
> **知识点**动态规划、记忆化搜索
66
7-
## 1. 动态规划
7+
## 1. 题目描述
88

9-
状态表示:
9+
给定一个整数数组 `prices`,其中 `prices[i]` 表示第 `i` 天的股票价格。要求计算能够获得的最大利润。
1010

11-
1. $f[i][0]$表示在第$i$天进行完操作不持有股票同时购买股票次数为0的最大利润
12-
2. $f[i][1]$表示在第$i$天进行完操作不持有股票同时购买股票次数为1的最大利润
13-
3. $f[i][2]$表示在第$i$天进行完操作不持有股票同时购买股票次数为2的最大利润
14-
4. $f[i][3]$表示在第$i$天进行完操作持有股票同时购买股票次数为0的最大利润
15-
5. $f[i][4]$表示在第$i$天进行完操作持有股票同时购买股票次数为1的最大利润
11+
**限制条件:**
1612

17-
状态计算:
13+
- 最多可以完成 **两笔** 交易(一笔交易指一次买入 + 一次卖出)。
14+
- 不能同时参与多笔交易:再次买入前必须先卖出当前持有的股票。
1815

19-
1. 若第$i$不持有股票,则有2种可能:
16+
**示例 1:**
2017

21-
- 第$i - 1$就没有股票,则
18+
- **输入**:prices = [3,3,5,0,0,3,1,4]
19+
- **输出**:6
20+
- **解释**:第 4 天买入(价格 0),第 6 天卖出(价格 3),利润 3;第 7 天买入(价格 1),第 8 天卖出(价格 4),利润 3。总利润 3 + 3 = 6。
2221

23-
$$f[i][0] = f[i - 1][0]$$
22+
**示例 2:**
2423

25-
$$f[i][1] = f[i - 1][1]$$
26-
27-
$$f[i][2] = f[i - 1][2]$$
24+
- **输入**:prices = [1,2,3,4,5]
25+
- **输出**:4
26+
- **解释**:第 1 天买入(价格 1),第 5 天卖出(价格 5),利润 4。只需一笔交易即可达到最大利润。
2827

29-
- 第$i - 1$持有股票,在第$i$天出售,则
30-
31-
$$f[i][0] = f[i - 1][2] + prices[i]$$
28+
**示例 3:**
3229

33-
$$f[i][1] = f[i - 1][3] + prices[i]$$
30+
- **输入**:prices = [7,6,4,3,1]
31+
- **输出**:0
32+
- **解释**:价格单调不升,无法通过交易获利,最大利润为 0。
3433

35-
对于$f[i][0]$的状态转移方程如下:
34+
**约束:**
3635

37-
$$
38-
f[i][j] = max(f[i - 1][j], f[i - 1][j + 3] + prices[i]), j \in [0, 1]
39-
$$
36+
- $1 \le \text{prices.length} \le 10^5$
37+
- $0 \le \text{prices}[i] \le 10^5$
4038

41-
$$
42-
f[i][2] = f[i - 1][2]
43-
$$
39+
---
4440

45-
2. 若第$i$持有股票,则有2种可能:
41+
## 2. 「动态规划 / 记忆化搜索」
4642

47-
- 第$i - 1$就持有股票,则
43+
下面代码用**记忆化搜索**实现:`dfs(i, j, hold)` 表示考虑前 `i+1` 天、剩余可完成 `j` 笔交易、当前是否持有股票时的最大利润,答案为 `dfs(n-1, 2, False)`
4844

49-
$$f[i][3] = f[i - 1][3]$$
45+
**复杂度分析:**
5046

51-
$$f[i][4] = f[i - 1][4]$$
47+
- 时间复杂度:$O(n)$(状态数 $O(n \times 2 \times 2)$,每状态 $O(1)$ 转移)
48+
- 空间复杂度:$O(n)$(递归栈与 cache,若用二维 DP 数组则为 $O(5n)$)
5249

53-
- 第$i - 1$不持有股票,在第$i$天购买,则
50+
**Python3**
5451

55-
$$f[i][3] = f[i - 1][1] - prices[i]$$
56-
57-
$$f[i][4] = f[i - 1][2] - prices[i]$$
58-
59-
对于$f[i][1]$的状态转移方程如下:
60-
61-
$$
62-
f[i][j] = max(f[i - 1][j], f[i - 1][j - 2] - prices[i]), j \in [3, 4]
63-
$$
64-
65-
最后答案为$\max(f[n][j]), j\in [0, 4]$。
66-
67-
- 时间复杂度:$O(n)$
68-
- 空间复杂度:$O(5n)$
69-
70-
```C++
71-
class Solution {
72-
public:
73-
long long f[100010][5];
74-
75-
int maxProfit(vector<int>& prices) {
76-
f[1][4] = -prices[0];
77-
f[1][0] = f[1][1] = f[1][3] = INT_MIN;
78-
for (int i = 2; i <= prices.size(); i ++)
79-
{
80-
f[i][0] = max(f[i - 1][0], f[i - 1][3] + prices[i - 1]);
81-
f[i][1] = max(f[i - 1][1], f[i - 1][4] + prices[i - 1]);
82-
f[i][2] = f[i - 1][2];
83-
f[i][3] = max(f[i - 1][3], f[i - 1][1] - prices[i - 1]);
84-
f[i][4] = max(f[i - 1][4], f[i - 1][2] - prices[i - 1]);
85-
}
86-
long long ans = 0;
87-
for (int i = 0; i < 5; i ++) ans = max(ans, f[prices.size()][i]);
88-
return ans;
89-
}
90-
};
91-
```
92-
93-
## 2. 滚动数组空间优化
94-
95-
可以发现第$i$个时刻的状态总依赖于第$i - 1$时刻的状态,所以可以使用5个常量保存上一个时刻的状态,这里不再演示
52+
```python
53+
class Solution:
54+
def maxProfit(self, prices: List[int]) -> int:
55+
@cache
56+
def dfs(i, j, hold):
57+
if j == 0:
58+
return 0
59+
if i < 0:
60+
return -inf if hold else 0
61+
if hold:
62+
return max(dfs(i - 1, j, True), dfs(i - 1, j - 1, False) - prices[i])
63+
return max(dfs(i - 1, j, False), dfs(i - 1, j, True) + prices[i])
64+
return dfs(len(prices) - 1, 2, False)
65+
```
Lines changed: 48 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,127 +1,69 @@
1-
# [188. 买卖股票的最佳时机 IV](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description/?envType=study-plan-v2&envId=dynamic-programming)
1+
# [188. 买卖股票的最佳时机 IV](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv/description)
22

3-
> **作者**弘树
4-
> **日期**2024-09-22
5-
> **所用时间**11min
3+
> **日期**2024-09-22、2026-02-28
4+
> **所用时间**11min
5+
> **知识点**动态规划、记忆化搜索
66
7-
## 1. 动态规划
7+
## 1. 题目描述
88

9-
### 1.1 状态表示
9+
给定一个整数 `k` 和一个整数数组 `prices`,其中 `prices[i]` 表示第 `i` 天的股票价格。要求计算能够获得的最大利润。
1010

11-
可以分为2种情况,分别为$f[i][0]$和$f[i][1]$
11+
**限制条件:**
1212

13-
对于$f[i][0]$来说:
13+
- 最多可以完成 **k 笔** 交易(一笔交易指一次买入 + 一次卖出)。
14+
- 不能同时参与多笔交易:再次买入前必须先卖出当前持有的股票。
1415

15-
1. $f[i][0][0]$表示在第$i$天进行完操作不持有股票同时购买股票次数为0的最大利润
16-
2. $f[i][0][1]$表示在第$i$天进行完操作不持有股票同时购买股票次数为1的最大利润
17-
3. ......
18-
4. $f[i][0][k]$表示在第$i$天进行完操作不持有股票同时购买股票次数为k的最大利润
16+
**示例 1:**
1917

20-
对于$f[i][1]$来说:
18+
- **输入**:k = 2, prices = [2,4,1]
19+
- **输出**:2
20+
- **解释**:第 1 天买入(价格 2),第 2 天卖出(价格 4),利润 2。
2121

22-
1. $f[i][1][0]$表示在第$i$天进行完操作不持有股票同时购买股票次数为0的最大利润
23-
2. $f[i][1][1]$表示在第$i$天进行完操作不持有股票同时购买股票次数为1的最大利润
24-
3. ......
25-
4. $f[i][1][k - 1]$表示在第$i$天进行完操作持有股票同时购买股票次数为$k - 1$的最大利润
22+
**示例 2:**
2623

27-
### 1.2 状态计算
24+
- **输入**:k = 2, prices = [3,2,6,5,0,3]
25+
- **输出**:7
26+
- **解释**:第 2 天买入(价格 2),第 3 天卖出(价格 6),利润 4;第 5 天买入(价格 0),第 6 天卖出(价格 3),利润 3。总利润 4 + 3 = 7。
2827

29-
#### 1.2.1 $f[i][0]$状态计算
28+
**约束:**
3029

31-
若第$i$不持有股票,则有2种可能:
30+
- $1 \le k \le 100$
31+
- $1 \le \text{prices.length} \le 1000$
32+
- $0 \le \text{prices}[i] \le 1000$
3233

33-
- 第$i - 1$就没有股票,则
34+
---
3435

35-
$$f[i][0][0] = f[i - 1][0][0]$$
36+
## 2. 「动态规划 / 记忆化搜索」
3637

37-
$$f[i][0][1] = f[i - 1][0][1]$$
38+
**思路:**
3839

39-
$$……$$
40-
41-
$$f[i][0][k] = f[i - 1][0][k]$$
40+
与「123. 买卖股票的最佳时机 III」相同,将**最多 2 笔**推广为**最多 k 笔**。用**记忆化搜索**`dfs(i, j, hold)` 表示考虑前 `i+1` 天、**剩余**可完成 `j` 笔交易、当前是否持有股票时的最大利润。
4241

43-
- 第$i - 1$持有股票,在第$i$天出售,则
44-
45-
$$f[i][0][0] = f[i - 1][1][0] + prices[i]$$
42+
-`j == 0`:不能再交易,返回 0。
43+
-`i < 0`:没有天数,持有则非法返回 $-\infty$,否则返回 0。
44+
- 若当前持有:今天可**不卖**`dfs(i-1, j, True)`)或**卖出**`dfs(i-1, j-1, False) + prices[i]`,用掉 1 笔交易)。
45+
- 若当前不持有:今天可**不买**`dfs(i-1, j, False)`)或**买入**`dfs(i-1, j, True) - prices[i]`)。
4646

47-
$$f[i][0][1] = f[i - 1][1][1] + prices[i]$$
47+
答案为 `dfs(len(prices)-1, k, False)`,即从最后一天、剩余 k 笔、不持有出发的最大利润。
4848

49-
$$……$$
49+
**复杂度分析:**
5050

51-
$$f[i][0][k - 1] = f[i - 1][1][k - 1] + prices[i]$$
51+
- 时间复杂度:$O(n \cdot k)$(状态 $(i, j, \text{hold})$ 共 $O(n \cdot k \cdot 2)$,每状态 $O(1)$ 转移)
52+
- 空间复杂度:$O(n \cdot k)$(递归栈与 cache)
5253

54+
**Python3**
5355

54-
综上,当$j \in [0, k - 1]$时,$f[i][0][j]$的状态转移方程为:
55-
56-
$$
57-
f[i][0][j] = max(f[i - 1][0][j], f[i - 1][1][j] + prices[i])
58-
$$
59-
60-
当$j = k$时,$f[i][0][j]$的状态转移方程为:
61-
62-
$$
63-
f[i][0][k] = f[i - 1][0][k]
64-
$$
65-
66-
#### 1.2.2 $f[i][1]$状态计算
67-
68-
若第$i$持有股票,则有2种可能:
69-
70-
- 第$i - 1$就持有股票,则
71-
72-
$$f[i][1][0] = f[i - 1][1][0]$$
73-
74-
$$f[i][1][1] = f[i - 1][1][1]$$
75-
76-
$$……$$
77-
78-
$$f[i][1][k - 1] = f[i - 1][1][k - 1]$$
79-
80-
- 第$i - 1$不持有股票,在第$i$天购买,则
81-
82-
$$f[i][1][0] = f[i - 1][0][1] - prices[i]$$
83-
84-
$$f[i][1][1] = f[i - 1][0][2] - prices[i]$$
85-
86-
$$……$$
87-
88-
$$f[i][1][k - 1] = f[i - 1][0][k] - prices[i]$$
89-
90-
对于$f[i][1]$的状态转移方程如下:
91-
92-
$$
93-
f[i][1][j] = max(f[i - 1][1][j], f[i - 1][0][j + 1] - prices[i]), j \in [0, k - 1]
94-
$$
95-
96-
最后答案为$\max(f[n][j]), j\in [0, k]$。
97-
98-
- 时间复杂度:$O(nk)$
99-
- 空间复杂度:$O(2nk)$
100-
101-
```C++
102-
class Solution {
103-
public:
104-
long long f[1010][2][110];
105-
106-
int maxProfit(int k, vector<int>& prices) {
107-
for (int i = 0; i < k; i ++) f[1][0][i] = INT_MIN;
108-
for (int i = 0; i < k - 1; i ++) f[1][1][i] = INT_MIN;
109-
f[1][0][k] = 0, f[1][1][k - 1] = -prices[0];
110-
for (int i = 2; i <= prices.size(); i ++)
111-
{
112-
for (int j = 0; j < k; j ++)
113-
f[i][0][j] = max(f[i - 1][0][j], f[i - 1][1][j] + prices[i - 1]);
114-
f[i][0][k] = f[i - 1][0][k];
115-
for (int j = 0; j < k; j ++)
116-
f[i][1][j] = max(f[i - 1][1][j], f[i - 1][0][j + 1] - prices[i - 1]);
117-
}
118-
long long ans = 0;
119-
for (int i = 0; i <= k; i ++) ans = max(ans, f[prices.size()][0][i]);
120-
return ans;
121-
}
122-
};
123-
```
124-
125-
## 2. 滚动数组空间优化
126-
127-
可以发现第$i$个时刻的状态总依赖于第$i - 1$时刻的状态,所以可以使用$2k$个常量保存上一个时刻的状态,这里不再演示
56+
```python
57+
class Solution:
58+
def maxProfit(self, k: int, prices: List[int]) -> int:
59+
@cache
60+
def dfs(i, j, hold):
61+
if j == 0:
62+
return 0
63+
if i < 0:
64+
return -inf if hold else 0
65+
if hold:
66+
return max(dfs(i - 1, j, hold), dfs(i - 1, j - 1, False) - prices[i])
67+
return max(dfs(i - 1, j, hold), dfs(i - 1, j, True) + prices[i])
68+
return dfs(len(prices) - 1, k, False)
69+
```
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# [1680. 连接连续二进制数字](https://leetcode.cn/problems/concatenation-of-consecutive-binary-numbers/description/)
2+
3+
> **日期**:2025-02-06
4+
> **所用时间**:10min
5+
> **知识点**:位运算、模拟、数学
6+
7+
## 1. 题目描述
8+
9+
给定一个整数 `n`,将 `1``n` 的二进制表示**按顺序连接**成一个二进制字符串,返回该二进制串对应的**十进制值**并对 $10^9 + 7$ **取模**的结果。
10+
11+
**示例 1:**
12+
13+
- **输入**:n = 1
14+
- **输出**:1
15+
- **解释**:只有数字 1,二进制为 `"1"`,十进制值为 1。
16+
17+
**示例 2:**
18+
19+
- **输入**:n = 3
20+
- **输出**:27
21+
- **解释**:1、2、3 的二进制分别为 `"1"``"10"``"11"`,连接后为 `"11011"`,对应十进制 $1 \times 2^4 + 1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 = 27$。
22+
23+
**示例 3:**
24+
25+
- **输入**:n = 12
26+
- **输出**:505379714
27+
- **解释**:连接得到的二进制串很长,其十进制值对 $10^9 + 7$ 取模后为 505379714。
28+
29+
**约束:**
30+
31+
- $1 \le n \le 10^5$
32+
33+
---
34+
35+
## 2. 「位运算 / 模拟」
36+
37+
**思路:**
38+
39+
- **方法一(模拟拼接)**:从 1 到 n 遍历,把每个数的二进制去掉前导 `"0b"` 后拼成一个字符串,最后用 `int(s, 2)` 转成十进制再对 $10^9+7$ 取模。实现简单,但字符串会很长,时间和空间都偏大。
40+
- **方法二(位运算)**:不显式构造字符串。维护当前结果 `ans`,对每个 $i$,把 `ans` 左移 `i.bit_length()` 位(为 $i$ 的二进制腾出位置),再与 $i$ 做按位或,即 `ans = (ans << i.bit_length() | i) % mod`。这样每次都是在“二进制串末尾”接上 $i$ 的二进制表示,等价于题目要求的连接,且无需字符串,空间 $O(1)$。
41+
42+
**复杂度分析:**
43+
44+
- 时间复杂度:$O(n)$(遍历 1 到 n,每次位运算与取模为 $O(1)$)
45+
- 空间复杂度:方法一 $O(n \log n)$(字符串长度),方法二 $O(1)$
46+
47+
**Python3**
48+
49+
```python
50+
class Solution:
51+
def concatenatedBinary(self, n: int) -> int:
52+
s = ""
53+
for i in range(1, n + 1):
54+
s += bin(i)[2:]
55+
return int(s, 2) % int(1e9 + 7)
56+
```
57+
58+
**Python3**
59+
60+
```python
61+
class Solution:
62+
def concatenatedBinary(self, n: int) -> int:
63+
ans = 0
64+
for i in range(1, n + 1):
65+
ans = (ans << i.bit_length() | i) % int(1e9 + 7)
66+
return ans
67+
```

0 commit comments

Comments
 (0)