Skip to content

Commit 5b08b37

Browse files
committed
feat: 20260302 check in
1 parent f3c6108 commit 5b08b37

3 files changed

Lines changed: 176 additions & 59 deletions

File tree

leetcode/3-动态规划(基础版)/6-买卖股票的最佳时间/309. 买卖股票的最佳时机含冷冻期.md

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,61 @@
11
# 309. 买卖股票的最佳时机含冷冻期
22

3-
> **作者:** 弘树
4-
> **版本:** 0.1
5-
> **日期:** 2024-08-06
6-
> **版权:** Copyright (c) 2024
7-
> **所用时间:** 53min
3+
> **日期**:2024-02-06
4+
> **所用时间**:53min
5+
> **知识点**:动态规划、状态机
86
9-
## 解题思路
10-
### 1.递归动态规划
7+
## 1. 题目描述
118

12-
每一个股票可以有2种选择:买或不买,如果买,那么后面2个位置的操作都唯一。如果不买,那么下一个操作仍然有2个选择。
9+
给定一个整数数组 `prices`,其中 `prices[i]` 表示第 `i` 天的股票价格。设计算法计算**最大利润**
10+
11+
**约束条件:**
12+
13+
- 不能同时参与多笔交易:再次买入前必须先卖出之前的股票。
14+
- **冷冻期**:卖出股票后,**第二天不能买入**(即至少休息一天才能再买)。
15+
16+
**示例:**
17+
18+
- **输入**:prices = [1,2,3,0,2]
19+
- **输出**:3
20+
- **解释**:对应交易序列为:第 1 天买入、第 2 天卖出、第 3 天冷冻、第 4 天买入、第 5 天卖出。利润 (2-1) + (2-0) = 3。
21+
22+
**约束:**
23+
24+
- $1 \le \text{prices.length} \le 5000$
25+
- $0 \le \text{prices}[i] \le 1000$
26+
27+
---
28+
29+
## 2. 动态规划
30+
31+
**思路:**
32+
33+
**三态**区分「不持股」的两种来源,避免冷冻期后立刻买入:
34+
35+
- **$f[i][0]$**:第 $i$ 天结束时**不持股**,且**不是因为当天卖出**而不持股(即昨天就不持股或昨天是冷冻期),此时的最大现金。
36+
- **$f[i][1]$**:第 $i$ 天结束时**持股**,此时的最大现金。
37+
- **$f[i][2]$**:第 $i$ 天结束时**不持股**,且**是因为当天卖出**而不持股(即进入冷冻期),此时的最大现金。
38+
39+
**转移:**
40+
41+
- $f[i][0] = \max(f[i-1][0], f[i-1][2])$:昨天不持股(状态 0)或昨天刚卖(状态 2),今天不买。
42+
- $f[i][1] = \max(f[i-1][1], f[i-1][0] - \text{prices}[i-1])$:昨天就持股,或昨天是状态 0(可买)今天买入。注意不能从状态 2 直接买(冷冻期)。
43+
- $f[i][2] = f[i-1][1] + \text{prices}[i-1]$:昨天持股,今天卖出。
44+
45+
**边界:** 第 1 天(下标 1):$f[1][0]=f[1][2]=0$,$f[1][1]=-\text{prices}[0]$。
46+
47+
**答案:** $\max_i \max(f[i][0], f[i][2])$(最后不持股的最大利润)。
48+
49+
**复杂度分析:**
50+
51+
- 时间复杂度:$O(n)$
52+
- 空间复杂度:$O(n)$(可压成 $O(1)$,只存前一天三个状态)
53+
54+
**递归 DP(思路:买/不买,买则后续两天唯一;易 TLE)**
1355

1456
- 时间复杂度:$O(3^n)$
1557
- 空间复杂度:$O(n)$
1658

17-
通过208/210个样例,剩下2个TLE。
18-
1959
```C++
2060
class Solution {
2161
public:
@@ -45,26 +85,7 @@ public:
4585
};
4686
```
4787
48-
### 2.迭代动态规划
49-
50-
怎么将递归形式的DP转化为迭代形式的DP,对于每个状态,应该维护2个属性:
51-
52-
1. 当前的利润和
53-
2. 当前所购买的股票
54-
55-
状态表示:$f[i][j]$表示在第$i$个时刻,购买股票价格为$j$,卖出股票时的最大利润
56-
57-
不对,感觉很乱,不知道怎么继续状态计算...,感觉状态表示肯定不对啊,不会/(ㄒoㄒ)/~~
58-
59-
---
60-
61-
上述的状态表示就错了,感觉好难,第一次做真的好难想到以下这种状态表示:
62-
63-
$f[i][0]$: 手上不持有股票,并且今天不是由于卖出股票而不持股,我们拥有的现金数
64-
65-
$f[i][1]$: 手上持有股票时,我们拥有的现金数
66-
67-
$f[i][2]$: 手上不持有股票,并且今天是由于卖出股票而不持股,我们拥有的现金数
88+
**迭代 DP(三态状态机,推荐)**
6889
6990
```C++
7091
class Solution {

leetcode/3-动态规划(基础版)/6-买卖股票的最佳时间/714. 买卖股票的最佳时机含手续费.md

Lines changed: 78 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,91 @@
11
# 714. 买卖股票的最佳时机含手续费
22

3-
> **作者:** 弘树
4-
> **日期:** 2024-09-14
3+
> **日期**:2024-09-14
4+
> **所用时间**:15min
5+
> **知识点**:动态规划、状态机
56
6-
## 解题思路
7-
### 1.动态规划
7+
## 1. 题目描述
88

9-
状态表示:
9+
给定一个整数数组 `prices`,其中 `prices[i]` 表示第 `i` 天的股票价格,以及一个非负整数 `fee` 表示**每笔交易的手续费**。可以**无限次**完成交易,求最大利润。
1010

11-
1. $f[i][0]$表示在第$i$天进行完操作不持有股票的最大利润
12-
2. $f[i][1]$表示在第$i$天进行完操作持有股票的最大利润
11+
**约束条件:**
1312

14-
状态计算:
13+
- 不能同时参与多笔交易:再次买入前必须先卖出之前的股票。
14+
- **每笔交易**(一次买入 + 一次卖出)需要支付一次手续费 `fee`。手续费可在**买入时****卖出时**扣除,两种约定等价(只需统一即可)。
15+
16+
**示例 1:**
17+
18+
- **输入**:prices = [1, 3, 2, 8, 4, 9], fee = 2
19+
- **输出**:8
20+
- **解释**:第 1 天买入(价格 1),第 4 天卖出(价格 8),利润 8-1-2=5;第 5 天买入(价格 4),第 6 天卖出(价格 9),利润 9-4-2=3。总利润 5+3=8。
21+
22+
**示例 2:**
23+
24+
- **输入**:prices = [1,3,7,5,10,3], fee = 3
25+
- **输出**:6
1526

16-
1. 若第$i$不持有股票,则有2种可能:
27+
**约束:**
1728

18-
- 第$i - 1$就没有股票,则$f[i][0] = f[i - 1][0]$
19-
- 第$i - 1$持有股票,在第$i$天出售,则$f[i][0] = f[i - 1][1] + prices[i] - fee$
20-
21-
对于$f[i][0]$的状态转移方程如下:
29+
- $1 \le \text{prices.length} \le 5 \times 10^4$
30+
- $1 \le \text{prices}[i] < 5 \times 10^4$
31+
- $0 \le \text{fee} < 5 \times 10^4$
2232

23-
$$
24-
f[i][0] = max(f[i - 1][0], f[i - 1][1] + prices[i] - fee)
25-
$$
33+
---
2634

27-
2. 若第$i$持有股票,则有2种可能:
35+
## 2. 动态规划
2836

29-
- 第$i - 1$就持有股票,则$f[i][1] = f[i - 1][1]$
30-
- 第$i - 1$持不持有股票,在第$i$天购买,则$f[i][1] = f[i - 1][0] - prices[i]$
37+
**思路:**
3138

32-
对于$f[i][1]$的状态转移方程如下
39+
与「122. 买卖股票的最佳时机 II」类似,无限次交易;区别在于每笔交易要扣手续费。用两态即可
3340

34-
$$
35-
f[i][1] = max(f[i - 1][1], f[i - 1][0] - prices[i])
36-
$$
41+
- **$f[i][0]$**:第 $i$ 天结束时**不持股**的最大利润。
42+
- **$f[i][1]$**:第 $i$ 天结束时**持股**的最大利润。
3743

38-
最后答案为$\max(f[i][0], f[i][1])$。
44+
**转移:**
45+
46+
- 不持股:昨天就不持股,或昨天持股、今天卖出(若在卖出时扣费,则 $f[i][0] = \max(f[i-1][0], f[i-1][1] + \text{prices}[i-1] - \text{fee})$)。
47+
- 持股:昨天就持股,或昨天不持股、今天买入(若在买入时扣费,则 $f[i][1] = \max(f[i-1][1], f[i-1][0] - \text{prices}[i-1] - \text{fee})$)。
48+
49+
两种扣费方式任选一种统一即可。答案取 $\max(f[n][0], f[n][1])$,实际为 $f[n][0]$(最后不持股更优)。
50+
51+
**复杂度分析:**
3952

4053
- 时间复杂度:$O(n)$
41-
- 空间复杂度:$O(n)$
54+
- 空间复杂度:$O(n)$(DP 数组);可滚动为两个变量,空间 $O(1)$
55+
56+
**记忆化搜索(Python)**
57+
58+
```python
59+
class Solution:
60+
def maxProfit(self, prices: List[int], fee: int) -> int:
61+
@cache
62+
def dfs(i, hold):
63+
if i < 0:
64+
return -inf if hold else 0
65+
if hold:
66+
return max(dfs(i - 1, hold), dfs(i - 1, False) - prices[i] - fee)
67+
return max(dfs(i - 1, hold), dfs(i - 1, True) + prices[i])
68+
return dfs(len(prices) - 1, False)
69+
```
70+
71+
**动态规划(C++)**
72+
73+
状态表示:
74+
75+
1. $f[i][0]$:第 $i$ 天进行完操作不持有股票的最大利润
76+
2. $f[i][1]$:第 $i$ 天进行完操作持有股票的最大利润
77+
78+
状态计算:
79+
80+
- $f[i][0] = \max(f[i-1][0], f[i-1][1] + \text{prices}[i-1] - \text{fee})$
81+
- $f[i][1] = \max(f[i-1][1], f[i-1][0] - \text{prices}[i-1])$
82+
83+
答案为 $\max(f[n][0], f[n][1])$。
84+
85+
- 时间复杂度:$O(n)$
86+
- 空间复杂度:$O(n)$
87+
88+
**C++**
4289

4390
```C++
4491
class Solution {
@@ -58,12 +105,14 @@ public:
58105
};
59106
```
60107

61-
### 2.滚动数组空间优化
108+
**滚动数组空间优化(C++)**
62109

63-
可以看到第$i$天的状态只依赖于第$i - 1$天,所以可以使用滚动数组的思想
110+
$i$ 天只依赖第 $i-1$ 天,可用两个变量滚动
64111

65-
- 时间复杂度:$O(n)$
66-
- 空间复杂度:$O(1)$
112+
- 时间复杂度:$O(n)$
113+
- 空间复杂度:$O(1)$
114+
115+
**C++**
67116

68117
```C++
69118
class Solution {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# [1536. 排布二进制网格的最少交换次数](https://leetcode.cn/problems/minimum-swaps-to-arrange-a-binary-grid/description/)
2+
3+
> **日期**:2026-03-02
4+
> **所用时间**:2min
5+
> **知识点**:贪心、模拟
6+
7+
## 1. 贪心
8+
9+
**思路:**
10+
11+
从上到下逐行确定。对第 $i$ 行,需要满足:该行从第 $i+1$ 列到最后一列全为 0,即 `sum(grid[i][i+1:]) == 0`
12+
13+
- 若当前第 $i$ 行已满足,则继续下一行。
14+
- 若不满足,则从第 $i+1$ 行起向下找**第一个**满足「从第 $i+1$ 列起全为 0」的行(即该行可以合法放在第 $i$ 行位置)。把该行通过**相邻行交换**逐格「冒泡」到第 $i$ 行,交换次数为该行当前下标与 $i$ 的差值;同时把原来的第 $i$ 行到该行前一行整体下移一格。
15+
- 若找不到这样的行,说明无法使网格有效,返回 $-1$。
16+
17+
实现时,用「把找到的那一行整行提到第 $i$ 行、其余行顺次下移」等价模拟多次相邻交换,并累加交换次数(每次相邻交换计 1,共 `t - i` 次)。
18+
19+
**复杂度分析:**
20+
21+
- 时间复杂度:$O(n^2)$(外层 $n$ 行,内层最坏 $O(n)$ 找行且每次 `sum(grid[j][i+1:])` 为 $O(n)$)
22+
- 空间复杂度:$O(1)$(仅用若干变量,若不计输入)
23+
24+
**Python3**
25+
26+
```python
27+
class Solution:
28+
def minSwaps(self, grid: List[List[int]]) -> int:
29+
ans = 0
30+
n = len(grid)
31+
32+
for i in range(n):
33+
if sum(grid[i][i + 1:]) == 0:
34+
continue
35+
t = -1
36+
for j in range(i + 1, n):
37+
if sum(grid[j][i + 1:]) == 0:
38+
t = j
39+
break
40+
if t == -1:
41+
return -1
42+
arr = grid[t][:]
43+
grid[i + 1: t + 1] = grid[i:t]
44+
grid[i] = arr
45+
ans += t - i
46+
return ans
47+
```

0 commit comments

Comments
 (0)