121. 买卖股票的最佳时机
题目描述
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
Related Topics 数组 动态规划
由于最多只交易一次,我们只需要找到当前天以前的股票最低价,也就能计算出当前天的最大利润。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
low = prices[0]
profit = 0
for i in range(1, n):
low = min(low, prices[i])
profit = max(profit, prices[i] - low)
return profit
下面我们尝试利用动态规划算法进行求解。
分析思路:
动态规划也就是穷举状态。对于每一天来说,我们有以下3种选择:不交易、买入、卖出。但是这3种状态并不是自由选择的,买入需要在卖出以后(第一次买入除外),卖出需要在买入以后。另外还需要考虑交易次数的限制。所以综合起来,我们的状态有3类:天数、交易次数、持股状态。
所以我们可以定义状态数组为 dp[i][j][k]
,表示第 i
天最多交易 j
次且持股状态为 k{0,1}
的状态下能获得的最大利润,其中 k=0
表示不持股,k=1
表示持股。
dp[i][j][0]
如何转移?
若不交易,则dp[i][j][0] = dp[i-1][j][0]
卖出后的不持股,则dp[i][j][0] = dp[i-1][j][1]+prices[i]
所以dp[i][j][0] = max( dp[i-1][j][0], dp[i-1][j][1]+prices[i])
dp[i][j][1]
如何转移?
若不交易,则dp[i][j][1] = dp[i-1][j][1]
买入后的持股,则dp[i][j][1] = dp[i-1][j][0]-prices[i]
所以dp[i][j][1] = max( dp[i-1][j][1], dp[i-1][j-1][0]-prices[i])
base case
第 0 天不持股,最大利润为0
dp[0][j][0] = 0
第 0 天持股,最大利润为-prices[0]
dp[0][j][1] = -prices[0]
交易次数为 0,不持股状态下利润为0,不可能为持股状态,故设置为负无穷。
dp[i][0][0] = 0
dp[i][0][1] = float('-inf')
输出
即第n-1
天最大交易次数下且不持股状态下的最大利润。
有了这套思路,对于买卖股票类问题,我们均可以套用。
回到题目,由于最多交易一次,即 j=1
,此时状态转移方程为:
由于交易次数这个维度均为1,所以可以省略,最后的状态转移方程为:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
dp = [[0, 0] for _ in range(n)]
# 初始化
dp[0][0] = 0
dp[0][1] = -prices[0]
# 遍历状态
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], -prices[i])
# print(dp)
return dp[n-1][0]
由于 dp[i]
只与 dp[i-1]
有关,进行状态压缩:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
sale = 0
buy = -prices[0]
for i in range(1, n):
sale = max(sale, buy + prices[i])
buy = max(buy, -prices[i])
return sale
122. 买卖股票的最佳时机 II
题目描述
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4
Related Topics 贪心算法 数组
此题与上一题的不同之处在于交易次数无限制。还是套用我们上面的分析思路,状态转移方程为
交易次数无限制,我们也可以理解为 j=+infinity
,此时 j-1
和 j
可以认为相等,即第二维同样可以省略,所以状态转移方程可写成:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# dp[i][k] 表示第i天持股状态为k{0: 不持股, 1: 持股}的最大利润
n = len(prices)
if n <= 1:
return 0
dp = [[0, 0] for _ in range(n)]
dp[0][1] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
dp[i][1] = max(dp[i-1][1], dp[i-1][0]-prices[i])
# print(dp)
return dp[n-1][0]
同样进行进行状态压缩:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
# dp[i][k] 表示第i天持股状态为k{0: 不持股, 1: 持股}的最大利润
n = len(prices)
if n <= 1:
return 0
sale = 0
buy = -prices[0]
for i in range(1, n):
sale = max(sale, buy+prices[i])
buy = max(buy, sale-prices[i])
return sale
123. 买卖股票的最佳时机 III
题目描述
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。
Related Topics 数组 动态规划
现在交易次数被限制为 k=2
,我们可以列举第二维的状态:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
dp = [[[0, 0] for _ in range(3)] for _ in range(n)]
# 初始化
dp[0][0][1] = float('-inf')
dp[0][1][1] = -prices[0]
dp[0][2][1] = -prices[0]
for i in range(1, n):
# dp[i][0][0] = 0
# dp[i][0][1] = float('-inf')
dp[i][1][0] = max(dp[i-1][1][0], dp[i-1][1][1]+prices[i])
dp[i][1][1] = max(dp[i-1][1][1], -prices[i])
dp[i][2][0] = max(dp[i-1][2][0], dp[i-1][2][1]+prices[i])
dp[i][2][1] = max(dp[i-1][2][1], dp[i-1][1][0]-prices[i])
return dp[n-1][2][0]
同样进行状态压缩:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
first_buy = -prices[0]
first_sale = float('-inf')
second_buy = float('-inf')
second_sale = float('-inf')
for i in range(1, n):
first_buy = max(first_buy, -prices[i])
first_sale = max(first_sale, first_buy+prices[i])
second_buy = max(second_buy, first_sale-prices[i])
second_sale = max(second_sale, second_buy+prices[i])
return second_sale
188. 买卖股票的最佳时机 IV
题目要求最多交易次数为k,针对前面几题的分析,我们可以对k进行分段讨论。
309. 最佳买卖股票时机含冷冻期
题目描述
给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:
输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
Related Topics 动态规划
还是套用前面的思路
class Solution:
def maxProfit(self, prices: List[int]) -> int:
n = len(prices)
if n <= 1:
return 0
dp = [[0, 0] for _ in range(n)]
# 初始化
dp[0][1] = -prices[0]
for i in range(1, n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i])
if i == 1:
dp[i][1] = max(dp[i-1][1], -prices[i])
else:
dp[i][1] = max(dp[i-1][1], dp[i-2][0]-prices[i])
return dp[n-1][0]
714. 买卖股票的最佳时机含手续费
参考
- labuladong 的解题。(放链接一直被吞)