股票买卖问题

Posted by CHuiL on September 2, 2019

股票问题

leetcode上的股票问题,归根到底就是给你一个数组,代表每一天股票的售价,问你买卖股票所能获得的最大利润,且每次交易只能卖了才能再次购买;不同题目会有不同的条件,如总共可以买卖k次,需要手续费,需要间隔一天等;

状态转移方程

每一天我们都有三个操作,即买入,卖出和无操作;对应这三个操作,每一天都有相应的状态,如某一天,在交易了k次之后,是否持有股票,已经在这个状态下所得到的最大利润;
可以用一个三维数组来表示,即dp[i][k][1] / dp[i][k][0] 表示在第i天,已交易k次的情况,且1,0 代表是否持有股票;
如dp[3][2][1] 的含义就是:今天是第三天,我现在手上持有着股票,至今最多进行 2 次交易。

状态转移方程如下

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]
#解释:今天我持有着股票,有两种可能:
#要么我昨天就持有着股票,然后今天选择 不操作,所以我今天还持有着股票;
#要么我昨天本没有持有,但今天我选择买了,所以今天我就持有股票了。

base case

 dp[-1][k][0] = 0
 解释:因为 i 是从 0 开始的,所以 i = -1 意味着还没有开始,这时候的利润当然是 0 。
 
 dp[-1][k][1] = -infinity
 解释:还没开始的时候,是不可能持有股票的,用负无穷表示这种不可能。
 
 dp[i][0][0] = 0
 解释:因为 k 是从 1 开始的,所以 k = 0 意味着根本不允许交易,这时候利润当然是 0 。
 
 dp[i][0][1] = -infinity
 解释:不允许交易的情况下,是不可能持有股票的,用负无穷表示这种不可能。

接下来先对应k的三种情况来套用这套公式

k=1

由于k=1,所以dp[i][1][1] = Max(dp[i-1][1][1],dp[i-1][0][0]-prices[i],而dp[i-1][0][0]=0 所以有dp[i][1][1] = Max(dp[i-1][1][1],-prices[i]
而且每个状态都直接和前一个状态有关,所以可以不用到整个数组。

func maxProfit(prices []int) int {
	buy_i_0 := 0 
	buy_i_1 := -99999
	for i:=1;i<=len(prices);i++{
		buy_i_0 = Max(buy_i_0,buy_i_1+prices[i-1])
		buy_i_1 = Max(buy_i_1,-prices[i-1])
	}

	return buy_i_0

}

k无限次

因为k无限制次数,所以可以认为 k 和 k - 1 是一样的;所以有

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

且这里每个状态都直接和前一个状态有关,所以可以不用到整个数组。

func maxProfit(prices []int) int {
    dp_i_0 := 0
    dp_i_1 := -99999
    for i:=0;i<len(prices);i++{
        tmp := dp_i_0
        dp_i_0 = Max(dp_i_0,dp_i_1+prices[i])
        dp_i_1 = Max(dp_i_1,tmp-prices[i])
    }
    return dp_i_0
    
}

k有限制次数

k有限制次数之后,就需要对k的状态进行遍历了。这里k=0的情况其实省略掉,直接从j=1开始,不过写上去方便理解一些;
这里多加了一个判断,当k大于天数的一半时,就可以理解为k为无限次了,因为买卖一次至少需要两天,所以当k大于天数的一半,就相当于无限次了;
就使用无限次的解法,不然当k过大,leetcode会报运行错误,超内存的错误;

func maxProfit(k int, prices []int) int {

    if k>(len(prices)/2){
        return maxProfit_inf(prices)
    }
    
    pro := make([][][2]int,len(prices)+1)
	
	for i:=0;i<=len(prices);i++{
		pro[i] = make([][2]int,k+1)
	}
	
    
	for i:=1;i<=len(prices);i++{
        for j:=0;j<=k;j++{
            if i-1==0{
                pro[i][j][0] = 0
                pro[i][j][1] = -prices[i-1]
                continue
            }
            
         if j==0{
             pro[i][j][0]=0
             pro[i][j][1]=-99999
             continue
         }
            
            pro[i][j][0] = Max(pro[i-1][j][0],pro[i-1][j][1]+prices[i-1])
            pro[i][j][1] = Max(pro[i-1][j][1],pro[i-1][j-1][0]-prices[i-1])
        }
        
	}
	return pro[len(prices)][k][0]

}

参考

一文团灭 LeetCode 股票买卖问题