1. 买卖股票的最佳时机 III
2. 题目解析
(此处原文包含图片,已移除)
3. 算法原理
状态表示: 以某一个位置为结尾或者以某一个位置为起点。dp[i] 表示:第 i 天结束之后,此时的最大利润。两种情况:
- f[i][j] 表示:第 i 天结束之后,完成了 j 次交易,处于买入状态,此时的最大利润。
- g[i][j] 表示:第 i 天结束之后,完成了 j 次交易,处于卖出状态,此时的最大利润。
状态转移方程: 在第 i-1 天处于买入状态,看买入状态能不能到自己,看卖出状态能不能到买入状态,另一个状态也是如此,一共 4 种状态。
- f[i][j] = max(f[i-1][j], g[i-1][j] - prices[i])
- g[i][j] = max(g[i-1][j], f[i-1][j-1] + prices[i])
初始化: 把 dp 表填满不越界,让后面的填表可以顺利进行。因为是在第 i-1 天处于买入/卖出状态,所以当交易次数为 0 时,就相当于在第 i 天为 -1,那么就会导致越界。所以我们可以修改一下第二个状态转移方程来判断一下,我们可以看到卖出状态到自己的情况是不会改变的,所以只用修改买入状态到卖出状态:
- g[i][j] = g[i-1][j](此状态一定不会越界)
- if(j-1>=0) g[i][j] = max(g[i][j], f[i-1][j-1] + prices[i])
在查找 f[i-1][j-1] + prices[i] 状态的时候先判断一下下标是否合法 (if(j-1>=0)),然后再求 max。 定义一个正无穷大/小的时候涉及到需要进行加减操作的时候,不要使用 INT_MIN/MAX,因为如果 INT_MIN 减去一个数的话就会变成一个非常大的整数而导致溢出,所以我们最好用 +/- 0x3f3f3f3f 来表示最小值。 本题初始化就是先将表里的所有值都初始化为 - 无穷大,再把 f[0][0] = -prices[0], g[0][0] = 0。
填表顺序: 本题的填表顺序是:从上往下填写每一行,每一行从左往右,两个表同时填。
返回值: 题目要求 + 状态表示。因为是要最大利润,所以买入状态不用考虑。本题的返回值是:g 表里最后一行里面的最大值。
4. 代码
动态规划的固定四步骤:
- 创建一个 dp 表
- 在填表之前初始化
- 填表(填表方法:状态转移方程)
- 确定返回值
class Solution {
public:
const int INF = 0x3f3f3f3f; // 将无穷大赋予给 INF
int maxProfit(vector<int>& prices) {
int n = prices.size();
// 1. 创建 dp 表
// 3:交易次数的三列:0,1,2,再将所有的位置都变成负无穷大
vector<vector<>> (n, <>(, -INF));
g = f;
f[][] = -prices[];
g[][] = ;
( i = ; i < n; i++) {
( j = ; j < ; j++)
{
f[i][j] = (f[i][j], g[i][j] - prices[i]);
g[i][j] = g[i][j];
(j >= ) g[i][j] = (g[i][j], f[i][j] + prices[i]);
}
}
ret = ;
( j = ; j < ; j++) ret = (ret, g[n][j]);
ret;
}
};


