买卖股票的时机

简介

买卖股票系列的问题,注意思考点是固定某一天卖出,可以得到子问题。

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

Go 代码实现_121

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]

Go 代码实现_122

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))

Go 代码实现_123

源代码附录

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)
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值