1. 题解
以下是各题题解,推荐先看问题分析,再看题解。
121. 买卖股票的最佳时机 - 力扣(LeetCode) 给定一个数组
prices
,它的第i
个元素prices[i]
表示一支给定股票第i
天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。 返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回0
。
js
var maxProfit = function(prices) {
let dp_i_0 = 0
let dp_i_1 = -prices[0]
const length = prices.length
for (let i = 1; i < length; i++) {
const tempDp_i_1 = dp_i_1
dp_i_1 = Math.max(dp_i_1, - prices[i])
dp_i_0 = Math.max(tempDp_i_1 + prices[i], dp_i_0)
}
return dp_i_0
};
2. 问题分析
有什么状态
i
: 天数j
: 持有、未持有- `k: 当前买入次数
dp[i][j][k]
: 最大利润
每个【状态】,可以做什么【选择】使得【状态】改变
- 目前为【持有】:
- 从上个状态:持有 -> 无操作 买入次数
k
- 从上个状态:未持有 -> 买入 买入次数
k - 1
- 从上个状态:持有 -> 无操作 买入次数
- 目前为【未持有】:
- 从上个状态:持有 -> 卖出 买入次数
k
- 从上个状态:未持有 -> 无操作 买入次数
k
- 从上个状态:持有 -> 卖出 买入次数
- 目前为【持有】:
如何定义 DP table/函数来表现【状态】和【选择】
Base Case 是什么
状态:第 i 天 j 状态下总共 k 次买入机会的最大利润
j 0 未持j有
1 jj有
选择:
持有 -> 无操作 -> 持有 持有次数 k
未持有 -> 买入 -> 持有 持有次数 k - 1
持有 -> 卖出 -> 未持有 持有次数 k
未持有 -> 无操作 -> 未持有 持有次数 k
baseCase:
dp[i][1][0] = -Infinity, 第 i 天,持有,未交易,不可能有这种情况,由于我们求最值,在此设置最小值,方便取最大值。
dp[i][1][0] = -prices[i] 第 i 天,持有,交易,利润 -
状态转移:
dp[i][1][k] = Math.max(dp[i - 1][1][k], dp[i - 1][0][k - 1] - prices[i])
dp[i][0][k] = Math.max(dp[i - 1][1][k] + prices[i], dp[i - 1][0][k])
最高利润 dp[i][0][...] = 0
当 k = 1
baseCase:
dp[i][1][0] = -Infinity, 第 i 天,持有,未交易,不可能有这种情况,由于我们求最值,在此设置最小值,方便取最大值。
dp[i][1][0] = -prices[i] 第 i 天,持有,交易,利润 -
dp[i][0][0] = 0 第 i 天,未持有,未交易,利润 0
dp[i][1][1] = Math.max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i])
= Math.max(dp[i - 1][1][1], - prices[i])
dp[i][0][1] = Math.max(dp[i - 1][1][1] + prices[i], dp[i - 1][0][1])
可以看出, 状态转移中,k 只会为 1 已经不重要了,忽略 k
dp[i][1] = Math.max(dp[i - 1][1], - prices[i])
dp[i][0] = Math.max(dp[i - 1][1] + prices[i], dp[i - 1][0])
根据状态表达式,再完善 baseCase:
dp[0][0] = 0, 第 0 天,未持有 0
dp[0][1] = -prices[i] 第 0 天,持有,利润 -