文章参考:一个方法团灭六道股票问题——作者:labuladong
.
.
.
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。
计算所有数对的差值。
class Solution:
def maxProfit(self, p) -> int:
Maxnet=0;n=len(p)
for i in range(0,n):
for j in range(i,n):
x=p[j]-p[i]
if x>Maxnet:
Maxnet=x
return Maxnet
class Solution:
def maxProfit(self, prices: List[int]) -> int:
profit = 0
if len(prices)< 2:
return profit
else:
profit = prices[1] - prices[0]
tmp = max(0,profit)
for i in range(2,len(prices)):
tmp += prices[i] - prices[i-1]
if tmp<0:
tmp = 0
elif tmp>profit:
profit = tmp
return max(profit,0)
执行用时 :44 ms, 在所有 Python3 提交中击败了94.13%的用户
内存消耗 :14.4 MB, 在所有 Python3 提交中击败了17.95%的用户
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 择优(选择1,选择2...)
代入具体问题:
这个问题的「状态」有三个,第一个是天数,第二个是允许交易的最大次数(这里仅允许交易一次),第三个是当前的持有状态(用 1 表示持有,0 表示没有持有)
每种状态下的选择:买入buy、卖出sell、保持不变rest
dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。
for 0 <= i < n:
for 1 <= k <= K:
for s in {0, 1}:
dp[i][k][s] = max(buy, sell, rest)
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
max( 选择 rest , 选择 sell )
解释:状态0有两种方式得到:1到0;0到0
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
max( 选择 rest , 选择 buy )
解释:状态1有两种方式得到:0到1;1到1
在两种转移可能里择优选择。
最后给转移方程做初始化:
dp[-1][k][0] = 0
解释:i = -1 意味着还没有开始,这时候利润是 0 。
dp[-1][k][1] = float('-inf')
解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
dp[i][0][0] = 0
解释:k = 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], dp[i-1][0][0] - prices[i])
= max(dp[i-1][1][1], -prices[i])
解释:k = 0 的 base case,所以 dp[i-1][0][0] = 0。
现在发现 k 都是 1,不会改变,即 k 对状态转移已经没有影响了。
可以进行进一步化简去掉所有 k:
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])
dp[i][1] = max(dp[i-1][1], -prices[i])
完整算法 I
class Solution:
def maxProfit(self, p) -> int:
n=len(p)
if n==0:return 0
dp=[[[]for j in range(0,2)]for i in range(0,n)]
#bace case:dp[-1][0]=0,dp[-1][1]=float('-int')
dp[0][0]=0;dp[0][1]=-p[0]
for i in range(1,n):
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + p[i]);
dp[i][1] = max(dp[i-1][1], -p[i])
return dp[n-1][0]
原博主提示到,新时刻的两种状态仅与上一刻的两种状态有关,所以不需要整个dp数组,只需要额外两个变量保存上一刻的状态即可。
完整算法 II
class Solution:
def maxProfit(self, p) -> int:
n=len(p)
dp_i_0=0;dp_i_1=float('-inf')
for i in range(0,n):
dp_i_0=max(dp_i_0, dp_i_1 + p[i])
dp_i_1 = max(dp_i_1, -p[i])
return dp_i_0
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
做一次遍历,计算所有满足条件的差价。
class Solution:
def maxProfit(self, prices: List[int]) -> int:
if not prices:return 0
net=0;buy=prices[0]
for i in prices[1:]:
if buy<i:net+=i-buy;buy=i
elif buy>i:buy=i
return net
class Solution:
def maxProfit(self, prices):
l = len(prices)
if l<=1:
return 0
maxp = 0
for i in range(1,l):
if prices[i]>prices[i-1]:
maxp+=prices[i]-prices[i-1]
return maxp
这道题与第一题的区别在于k无限大。
易得到状态转移方程:
dp[i][k][0] = max(dp[i-1][k][0], dp[i-1][k][1] + prices[i])
dp[i][k][1] = max(dp[i-1][k][1], dp[i-1][k-1][0] - prices[i])
= max(dp[i-1][k][1], dp[i-1][k][0] - prices[i])
正如k=1时k对方程无影响,k=正无穷时对方程也没有影响,可略去。
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])
完整算法 I
class Solution:
def maxProfit(self, p) -> int:
n=len(p)
#特殊情况:p=[]
if n==0:return 0
#给出dp数组的初始化
dp=[[[]for j in range(0,2)]for i in range(0,n)]
#bace case
dp[0][0]=0;dp[0][1]=-p[0]
for i in range(1,n):
temp=dp[i-1][0] #!!!注意
dp[i][0] = max(dp[i-1][0], dp[i-1][1] + p[i]);
dp[i][1] = max(dp[i-1][1], temp-p[i])
return dp[n-1][0]
完整算法 II
class Solution:
def maxProfit(self, p) -> int:
n=len(p)
if n==0:return 0
dp_i_0=0;dp_i_1=-p[0]
for i in range(1,n):
temp=dp_i_0
dp_i_0 = max(dp_i_0, dp_i_1 + p[i])
dp_i_1 = max(dp_i_1, temp-p[i])
return dp_i_0
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
这里的K=2。K=1或无穷时可以忽略,除此以外则必须加入穷举。
class Solution:
def maxProfit(self, p) -> int:
k_max = 2
n = len(p)
# dp1 = [[[[]] * 2] * (k_max + 1)] * n
#苦笑。一直做到这里才发现一个巨大的坑,python里通过乘法得到的嵌套列表,
#修改一个则全部都会变……度娘了一下,发现使用列表生成器可以避免这种错误
dp=[[[[] for i in range(0,2)] for j in range(0,k_max+1)]for k in range(0,n)]
for i in range(0, n):
for k in range(k_max, 0, -1):
if i == 0:
dp[i][k][0] = 0
dp[i][k][1] = -p[i]
continue
#k=0时是特殊情况,需要声明
if k - 1 == 0:
# dp[i-1][0][1]=0
dp[i - 1][0][0] = 0
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + p[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - p[i])
#print('dp[{0}][{1}][0]= max(dp[{3}][{1}][0], dp[{3}][{1}][1] + p[{0}]):{2},'.format(i,k,dp[i][k][0],i-1),'dp[{0}][{1}][1]= max(dp[{3}][{1}][1], dp[{3}][{4}][0] + p[{0}]):{2}'.format(i,k,dp[i][k][0],i-1,k-1))
return dp[n - 1][k_max][0]
这里 k 取值范围比较小,所以可以不用 for 循环,直接把 k = 1 和 2 的情况手动列举出来也可以:
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])
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])
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp_i_10=0
dp_i_11=float('-inf')
dp_i_20=0
dp_i_21=float('-inf')
for n in prices:
dp_i_20=max(dp_i_20,dp_i_21+n)
dp_i_21=max(dp_i_21,dp_i_10-n)
dp_i_10=max(dp_i_10,dp_i_11+n)
dp_i_11=max(dp_i_11,-n)
return dp_i_20
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
这是前三题的综合:
class Solution:
def maxProfit(self, k_max: int, p: List[int]) -> int:
n=len(p)
if n==0 or k_max==0:return 0#两种特殊情况
if k_max==1:#=1时
dp_i_0=0;dp_i_1=-p[0]
for i in range(1,n):
dp_i_0=max(dp_i_0,dp_i_1+p[i])
dp_i_1=max(dp_i_1,-p[i])
return dp_i_0
if k_max>n//2:#k可视为无限时
dp_i_0=0;dp_i_1=-p[0]
for i in range(1,n):
temp=dp_i_0
dp_i_0=max(dp_i_0,dp_i_1+p[i])
dp_i_1=max(dp_i_1,temp-p[i])
return dp_i_0
else:# 123 题的情况
dp=[[[[]for i in range(0,2)]for j in range(0,k_max+1)]for k in range(0,n)]
for i in range(0,n):
for k in range(k_max,0,-1):
if i==0:
dp[i][k][0]=0
dp[i][k][1]=-p[i]
continue
if k-1==0:
dp[i-1][0][0]=0
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + p[i])
dp[i][k][1] = max(dp[i - 1][k][1], dp[i-1][k-1][0]-p[i])
return dp[n-1][k_max][0]
观摩了一下大佬的答案,发现dp数组的建立是多余的。
class Solution:
def maxProfit(self, k_max: int, p: List[int]) -> int:
n=len(p)
if n==0 or k_max==0:return 0#两种特殊情况
# if k_max==1:#=1时
# dp_i_0=0;dp_i_1=-p[0]
# for i in range(1,n):
# dp_i_0=max(dp_i_0,dp_i_1+p[i])
# dp_i_1=max(dp_i_1,-p[i])
# return dp_i_0
if k_max>n//2:#k可视为无限时
dp_i_0=0;dp_i_1=-p[0]
for i in range(1,n):
temp=dp_i_0
dp_i_0=max(dp_i_0,dp_i_1+p[i])
dp_i_1=max(dp_i_1,temp-p[i])
return dp_i_0
else:# 123 题的情况
dp=[[[]for i in range(0,2)]for j in range(0,k_max+1)]
for i in range(0,n):
for k in range(k_max,0,-1):
if i==0:
dp[k][0]=0
dp[k][1]=-p[i]
continue
if k-1==0:
dp[0][0]=0
dp[k][0] = max(dp[k][0], dp[k][1] + p[i])
dp[k][1] = max(dp[k][1], dp[k-1][0]-p[i])
return dp[k_max][0]
class Solution:
def maxProfit(self, k: int, prices: List[int]) -> int:
if len(prices) <= 1: return 0
if (k < len(prices) // 2) :
dp = [[-prices[0], 0] for i in range(k+1)]
for price in prices[1:]:
for i in range(1, k+1):
dp[i] = [max(dp[i][0], dp[i-1][1]-price), max(dp[i][1], dp[i][0]+price)]
return dp[k][1]
else:
dp = [-prices[0], 0]
for price in prices[1:]:
dp = [max(dp[0], dp[1]-price), max(dp[1], dp[0]+price)]
return dp[1]
作者:hellozhaozheng
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/solution/tong-yong-fang-fa-de-chao-jian-ji-shi-xian-fu-che-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。