题目描述:
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)即每个时刻只能有一股
各个题目的区别:
121——买卖一次
122——买卖无数次
123——最多买卖两次
188——买卖k次
309——有冷冻期,但是可以买卖无数次
714——每买卖一次有手续费,但是可以买卖无数次
思路:
动态规划,不同难度的问题,dp状态定义的维度和状态转移方程也是不一样的。
注意以下代码都是为了系统地比较各个问题的区别,可能每一段代码都可以进一步简化空间复杂度,但是dp的思路都是一样的。
121——买卖一次
dp[i]:到了第i 天的最大利润
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
if not prices:
return 0
res = 0
# 注意初始化的方式
proit = [[0 for i in range(3)] for i in range(len(prices))]
print(proit)
proit[0][0], proit[0][1], proit[0][2] = 0, -prices[0], 0
for d in range(1, len(prices)):
# 三个状态分别是:没有买卖,已经买入一股,已经卖出一股
proit[d][0] = proit[d-1][0]
proit[d][1] = max(proit[d-1][1], proit[d-1][0] - prices[d])
proit[d][2] = proit[d-1][1] + prices[d]
res = max(res, proit[d][0], proit[d][1], proit[d][2])
return res
122——买卖无数次:
dp[i][0]: 到了第i 天的最大利润,且此时手里没有股票,故可以选择买和不买
dp[i][1]:到了第i 天的最大利润,且此时手里有股票,故可以选择卖或者不卖
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
res = 0
proit = [[0 for i in range(2)] for i in range(len(prices))]
proit[0][0], proit[0][1] = 0, -prices[0]
for d in range(1, len(prices)):
# m每次可以考虑买和卖两种情况
proit[d][0] = max(proit[d-1][0], proit[d-1][1] + prices[d])
proit[d][1] = max(proit[d-1][1], proit[d-1][0] - prices[d])
res = max(res, proit[d][0])
return res
123——最多买卖两次
dp[i][k][0] : 第i 天的利润,此时已经完成交易 k 次,手里没有股票
dp[i][k][0] : 第i 天的利润,此时已经完成交易 k 次,手里有股票
所以这里k有0,1,2三种可能情况,直接列举出来,这样减少一轮循环;
同样本题可以采用188的代码,将K设置为2。
import sys
maxint = sys.maxsize
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
res = 0
proit = [[[0 for i in range(2)] for i in range(3)] for i in range(len(prices))]
proit[0][0][0], proit[0][0][1] = 0, -prices[0]
proit[0][1][0], proit[0][1][1] = -maxint, -maxint
proit[0][2][0], proit[0][2][1] = -maxint, -maxint
for d in range(1, len(prices)):
proit[d][0][0] = proit[d-1][0][0]
proit[d][0][1] = max(proit[d-1][0][1], -prices[d])
proit[d][1][0] = max(proit[d-1][1][0], proit[d-1][0][1] + prices[d])
proit[d][1][1] = max(proit[d-1][1][1], proit[d-1][1][0] - prices[d])
proit[d][2][0] = max(proit[d-1][2][0], proit[d-1][1][1] + prices[d])
proit[d][2][1] = max(proit[d-1][2][1], proit[d-1][2][0] - prices[d])
res = max(res, proit[d][0][0], proit[d][1][0], proit[d][2][0])
return res
188——买卖k次
dp[i][k][0] : 第i 天的利润,此时已经完成交易 k 次,手里没有股票
dp[i][k][0] : 第i 天的利润,此时已经完成交易 k 次,手里有股票
import sys
maxint = sys.maxsize
class Solution(object):
def maxProfit(self, k, prices):
"""
:type k: int
:type prices: List[int]
:rtype: int
"""
if not prices:
return 0
res = 0
proit = [[[0 for i in range(2)] for i in range(k+1)] for i in range(len(prices))]
proit[0][0][0], proit[0][0][1] = 0, -prices[0]
for j in range(1, k):
proit[0][j][0], proit[0][j][1] = -maxint, -maxint
for d in range(1, len(prices)):
proit[d][0][0] = proit[d-1][0][0]
proit[d][0][1] = max(proit[d-1][0][1], -prices[d])
for w in range(1, k+1):
proit[d][w][0] = max(proit[d-1][w][0], proit[d-1][w-1][1] + prices[d])
proit[d][w][1] = max(proit[d-1][w][1], proit[d-1][w][0] - prices[d])
res = max(res, proit[d][w][0], proit[d][0][0])
return res
309——有冷冻期,但是可以买卖无数粗
状态定义和转移详见代码
import sys
maxint = sys.maxsize
class Solution(object):
def maxProfit(self, prices):
"""
:type prices: List[int]
:rtype: int
"""
if not prices or len(prices) == 1:
return 0
res = 0
proit = [[0 for i in range(3)] for i in range(len(prices))]
'''
每天会有两种状态,持有和不持有,但是由于存在冷冻期,将不持有的情况分为两种:
1. 会导致第二天冷冻期
2. 不会导致第二天冷冻期
'''
proit[0][0], proit[0][1], proit[0][2] = 0, -prices[0], -maxint
# 1-持有一股 0-不持有,不会导致第二天处于冷冻期 2-不持有,会导致第二天处于冷冻期
for d in range(1, len(prices)):
proit[d][1] = max(proit[d-1][1], proit[d-1][0]-prices[d])
proit[d][0] = max(proit[d-1][0], proit[d-1][2])
proit[d][2] = proit[d-1][1]+prices[d]
res = max(res, proit[d][0], proit[d][2])
return res
714——每买卖一次有手续费,但是可以买卖无数次
同122很像,只是多了一个手续费,体现在状态转移方程那里。
import sys
maxint = sys.maxsize
class Solution(object):
def maxProfit(self, prices, fee):
"""
:type prices: List[int]
:type fee: int
:rtype: int
"""
if not prices or len(prices) == 1:
return 0
res = 0
proit = [[0 for i in range(2)]for i in range(len(prices))]
proit[0][0], proit[0][1] = 0, -prices[0]
for d in range(1, len(prices)):
proit[d][0] = max(proit[d-1][0], proit[d-1][1]+prices[d]-fee)
proit[d][1] = max(proit[d-1][1], proit[d-1][0]-prices[d])
res = max(res, proit[d][0], proit[0][0], proit[d][1])
return res
扩展:
在现在的研究问题中,每个时刻只能持有一股,若题目变成可以持有n股,则:
dp[i][k][j]: j 从{0,1}变成0~j,此时状态转移:
dp[i][k][j] = max{
dp[i-1][k][j]; 不动
dp[i-1][k-1][j+1]+q[i]; 卖
dp[]i-1][k][j-1] - q[i]; 买
}
这时状态转移变得复杂,需要注意数组越界问题等。