简介
买卖股票系列的问题,注意思考点是固定某一天卖出,可以得到子问题。
121. 买卖股票的最佳时机 (Easy)
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/
题目:给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
解答:正如其难度,Easy。只需要计算出以每一天为卖出点的最大值,再取最大值即可。设f(k)表示卖出A[k],则f(k) = A[k] - min{A[0],A[1],…,A[k-1]}
显然,记min(k) = min{A[0],A[1],…,A[k]}, 则有 min(k) = min(min(k-1),A[k])
maxPrice(A): n = A.length m = A[0] f = 0 for i=1 to n: if f < A[i] - m: f = A[i] - m m = min(m, A[i]) return f
122. 买卖股票的最佳时机 II (Medium)
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/
题目:给定一个数组 prices ,其中 prices[i] 是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
解答:令g(k)表示A[k]被卖掉的[0~k]的最大股票收益。令f(k)表示[0~k]的最大收益。
则 g(k) = max|0<=i<k|{ f(i-1), A[k]-A[i] }。其含义是:选择[0~k-1]中的任何一个作为与A[k]匹配的买入日期,计算最大值。
最终: f(k) =max{g(k), f(k-1), 0} ,也就是要么k被卖掉,要么k不卖。
由于g(k)取最大值,因此 g(k)=max{g(k-1) + A[k]-A[k-1], A[k] - A[k-1] + f(i-1)}.
maxPriceII(A): n = A.length f = makeArray(length => n, init => 0) g = 0 for k = 1 to n: g = max(g + A[k]-A[k-1], f[k-1] + A[k] - A[k-1]) f[k] = max(g, f[k-1),0) return f[n-1]
123. 买卖股票的最佳时机 III (Hard)
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/
题目:给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
解答:f(k,T) 表示至多T次交易时的最大值。
- 如果A[k]卖出,则 f(k,T) => max|0<=i<k|{A[k]-A[i] + f(i-1,T-1) }
可以将此分支记为g(k,T),则有:
g(k+1,T)
= max|0<=i<k+1|{A[k+1]-A[i] + f(i-1,T-1) }
= max{A[k+1]-A[k] + f(k-1,T-1), A[k+1] - A[k] + max|0<=i<k|{A[k]-A[i] + f(i-1,T-1)}}
= max{A[k+1]-A[k] + f(k-1,T-1), A[k+1] - A[k] + g(k,T)}- 如果A[k]不卖出,则f(k,T) => f(k-1,T)
所以:f(k,T) = max(f(k-1,T), g(k,T),0)
基于top-bottom的写法(不带备忘录):maxPriceIII(A): f(k,T): if k<=0 || T<=0: return 0 return max(f(k-1,T), g(k,T),0) g(k,T): if k<=0 || T<=0: return 0 return max(A[k]-A[k-1] + f(k-2,T-1), A[k]-A[k-1]+g(k-1,T))
源代码附录
121. 买卖股票的最佳时机
func maxProfit(prices []int) int {
n := len(prices)
m := prices[0]
f := 0
for i:=1;i<n;i++{
if f < prices[i]-m{
f = prices[i]-m
}
if m > prices[i] {
m = prices[i]
}
}
return f
}
122. 买卖股票的最佳时机 II (Medium)
func maxProfit(prices []int) int {
// g(k) means k is selled, max gain, range [0,k]
// g(k) = max|0<=i<k|{ f(i-1), A[k]-A[i] }
//
// f(k) means max gain range [0,k]
// f(k) = max(g(k),f(k-1))
//
// then:
// f(k) = max|0<=i<k|{A[k]-A[i]+f(i-1) }, f(k-1),0 // no k involvled
// f(0) = 0
// f(1) = A1 - A0
// f(2) =
// maintain A[k]-A[i] + f(i-1) as maxOpt,because it is the max|0<=i<k| part,which can be inherited
// A[k+1] - A[k] + f(k)
//
n := len(prices)
dp:=make([]int,n)
maxOpt := 0
for i:=1;i<n;i++{
maxOpt = max(prices[i] - prices[i-1] + dp[i-1], maxOpt + prices[i] -prices[i-1])
dp[i] = max(maxOpt,dp[i-1])
dp[i] = max(dp[i],0)
}
return dp[n-1]
}
func max(a int,b int) int {
if a > b {
return a
}
return b
}
123. 买卖股票的最佳时机 III
注意:下面的实现topBottomMemo版本:
执行用时:1300 ms, 在所有 Go 提交中击败了5.75%的用户 内存消耗:36.9 MB, 在所有 Go 提交中击败了5.16%的用户 通过测试用例: 214 / 214
可以说处于PASS的最差状态。
而naiveCompute则超时。
TODO:我们应当如何选择计算的路径,来优化这个算法?
func maxProfit(prices []int) int {
// f(k,T) means when we can do T transactions, the maximum output
// if k is sold, then
// f(k,T) => max|0<=i<k|{A[k]-A[i] + f(i-1,T-1) }
// let it be g(k,T),so g(k,T) = max|0<=i<k|{A[k]-A[i] + f(i-1,T-1) }
// = A[k] - A[i'] + f(i'-1, T-1)
// g(k+1,T) = max|0<=i<k+1|{A[k+1]-A[i] + f(i-1,T-1) } = max{A[k+1]-A[k] + f(k-1,T-1), A[k+1] - A[k] + max|0<=i<k|{A[k]-A[i] + f(i-1,T-1)}} = max{A[k+1]-A[k] + f(k-1,T-1), A[k+1] - A[k] + g(k,T)}
//
// if k is not sold,then
// f(k,T) => f(k-1,T)
// so, f(k,T) = max(f(k-1,T), g(k,T),0)
// utilization(maybe, bottom-top, not realized yet):
// f(k,T) => g(0,T) ... g(k-1,T) g(k,T) max
// g(0,0), g(1,0) => 0
// g(0,1), g(1,1) => f(*,1)
//
return topBottomMemo(prices)
}
func topBottomMemo(prices []int) int {
// with cache top-bottom
A := prices
n := len(A)
var f func(k int,T int) int
var g func(k int,T int) int
dp := make([][]int, n)
for i:=0;i<n;i++{
dp[i] = make([]int,2)
dp[i][0]=-1
dp[i][1]=-1
}
dpf := make([][]int,n)
for i:=0;i<n;i++{
dpf[i] = make([]int,2)
dpf[i][0]=-1
dpf[i][1]=-1
}
f = func(k int,T int) int {
if k <= 0 || T<=0{
return 0
}
if dpf[k][T-1]!=-1{
return dpf[k][T-1]
}
dpf[k][T-1] = max3(f(k-1,T), g(k,T),0)
return dpf[k][T-1]
}
g = func(k int,T int) int {
if k <= 0 || T<=0{
return 0
}
if dp[k][T-1]!=-1{
return dp[k][T-1]
}
dp[k][T-1] =max(A[k]-A[k-1] + f(k-1,T-1),A[k]-A[k-1] + g(k-1,T))
return dp[k][T-1]
}
return f(n-1,2)
}
func naiveCompute(prices []int) int {
// completely follow the no cache mode
A := prices
n := len(A)
var f func(k int,T int) int
var g func(k int,T int) int
f = func(k int,T int) int {
if k <= 0 || T<=0{
return 0
}
return max3(f(k-1,T), g(k,T),0)
}
g = func(k int,T int) int {
if k <= 0 || T<=0{
return 0
}
return max(A[k]-A[k-1] + f(k-1,T-1),A[k]-A[k-1] + g(k-1,T))
}
return f(n-1,2)
}
func max(a int,b int) int {
if a>b{
return a
}
return b
}
func max3(a int,b int,c int) int {
return max(max(a,b),c)
}