【LeetCode】Best Time to Buy and Sell Stock总结

LeetCode上买卖股票的问题如下:

  1. 121. Best Time to Buy and Sell Stock
  2. 122. Best Time to Buy and Sell Stock II
  3. 123. Best Time to Buy and Sell Stock III
  4. 188. Best Time to Buy and Sell Stock IV
  5. 309. Best Time to Buy and Sell Stock with Cooldown
  6. 714. Best Time to Buy and Sell Stock with Transaction Fee

可以统一为同一种dp状态,可以用T[i][k]表示第i天为止最多k次交易最大利润,因为当天交易状态最后有两种:一种是持有股票,另一种是没有股票,因此可以细分为两种状态:T[i][k][0],T[i][k][1]

状态转移方程为:

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])

T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i])

把状态归纳为:第i天完成最多k次交易,0表示手上没有股票,1表示有:这里有没有股票的状态具有一般性,实际上就是dp状态跟当前值有没有关系

上述六个股票问题可以按k的值进行分类,k是允许交易的最大数量(最后两个相当于加了额外条件,如“冷却时间”或“交易费”)。

 121. Best Time to Buy and Sell Stock

k=1的情况,

T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + prices[i])
T[i][1][1] = max(T[i-1][1][1], T[i-1][0][0] - prices[i]) = max(T[i-1][1][1], -prices[i])

 可以进一步优化为两个变量:

public int maxProfit(int[] prices) {
    int T_i10 = 0, T_i11 = Integer.MIN_VALUE;
        
    for (int price : prices) {
        T_i10 = Math.max(T_i10, T_i11 + price);
        T_i11 = Math.max(T_i11, -price);
    }
        
    return T_i10;
}

 122. Best Time to Buy and Sell Stock II

k=oo,意味着k和k-1状态一样

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k-1][0] - prices[i]) = max(T[i-1][k][1], T[i-1][k][0] - prices[i])

同样可以优化空间:

public int maxProfit(int[] prices) {
    int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
    
    for (int price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = Math.max(T_ik0, T_ik1 + price);
        T_ik1 = Math.max(T_ik1, T_ik0_old - price);
    }
    
    return T_ik0;
}

 123. Best Time to Buy and Sell Stock III

k=2的情况:T[i][1][0]T[i][1][1]T[i][2][0]T[i][2][1]可以直接用4个变量表示

T[i][2][0] = max(T[i-1][2][0], T[i-1][2][1] + prices[i])
T[i][2][1] = max(T[i-1][2][1], T[i-1][1][0] - prices[i])
T[i][1][0] = max(T[i-1][1][0], T[i-1][1][1] + prices[i])
T[i][1][1] = max(T[i-1][1][1], -prices[i])
public int maxProfit(int[] prices) {
    int T_i10 = 0, T_i11 = Integer.MIN_VALUE;
    int T_i20 = 0, T_i21 = Integer.MIN_VALUE;
        
    for (int price : prices) {
        T_i20 = Math.max(T_i20, T_i21 + price);
        T_i21 = Math.max(T_i21, T_i10 - price);
        T_i10 = Math.max(T_i10, T_i11 + price);
        T_i11 = Math.max(T_i11, -price);
    }
        
    return T_i20;
}

188. Best Time to Buy and Sell Stock IV

k为任意值,这是最常见的情况,因此每天我们需要在当天结束时用对应于0或1只股票的不同k值更新所有最大利润。但是,如果k超过某个临界值,我们可以做一些小的优化,超过这个值,最大利润不会长期取决于允许的交易数量,而是受可用股票数量的限制(价格数组的长度)

public int maxProfit(int k, int[] prices) {
    if (k >= prices.length >>> 1) {
        int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
    
        for (int price : prices) {
            int T_ik0_old = T_ik0;
            T_ik0 = Math.max(T_ik0, T_ik1 + price);
            T_ik1 = Math.max(T_ik1, T_ik0_old - price);
        }
        
        return T_ik0;
    }
        
    int[] T_ik0 = new int[k + 1];
    int[] T_ik1 = new int[k + 1];
    Arrays.fill(T_ik1, Integer.MIN_VALUE);
        
    for (int price : prices) {
        for (int j = k; j > 0; j--) {
            T_ik0[j] = Math.max(T_ik0[j], T_ik1[j] + price);
            T_ik1[j] = Math.max(T_ik1[j], T_ik0[j - 1] - price);
        }
    }
        
    return T_ik0[k];
}

309. Best Time to Buy and Sell Stock with Cooldown.

k = +Infinity but with cooldown

没有冷却的转移情况是:

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])

但是在“冷却时间”下,如果在第(i-1)天出售股票,我们就无法在第i天购买。因此,在上面的第二个等式中,如果我们打算在第i天购买,我们实际上应该使用T [i-2] [k] [0]而不是T [i-1] [k] [0]。这里冷冻期是在买之前,所以卖的时候不用考虑,第二个等式才要考虑:注意虽然这里T[i-1][k][0]不一定在i-1天有卖股票但是这种情况也适合于T[i-2][k][0],也就是i-2天包含了i-1中不卖股票的情况所以是正确的

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-2][k][0] - prices[i])

public int maxProfit(int[] prices) {
    int T_ik0_pre = 0, T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
    
    for (int price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = Math.max(T_ik0, T_ik1 + price);
        T_ik1 = Math.max(T_ik1, T_ik0_pre - price);
        T_ik0_pre = T_ik0_old;
    }
    
    return T_ik0;
}

714. Best Time to Buy and Sell Stock with Transaction Fee

k = +Infinity but with transaction fee

同样,这个同情况2一样,因为它们具有相同的k值,除了现在需要稍微修改递归关系以考虑“交易费”要求。案例II的原始复发关系由下式给出

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])

由于现在我们需要为每笔交易支付一些费用(表示为费用),因此在第i天购买或出售股票后的利润应减去这个数额,因此新的递推关系:

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i])
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i] - fee)

或者

T[i][k][0] = max(T[i-1][k][0], T[i-1][k][1] + prices[i] - fee)
T[i][k][1] = max(T[i-1][k][1], T[i-1][k][0] - prices[i])

public int maxProfit(int[] prices, int fee) {
    int T_ik0 = 0, T_ik1 = Integer.MIN_VALUE;
    
    for (int price : prices) {
        int T_ik0_old = T_ik0;
        T_ik0 = Math.max(T_ik0, T_ik1 + price);
        T_ik1 = Math.max(T_ik1, T_ik0_old - price - fee);
    }
        
    return T_ik0;
}

 

你可能感兴趣的:(动态规划)