【代码训练营】day49 | 123.买卖股票的最佳时机III & 188.买卖股票的最佳时机IV

所用代码 java

买卖股票的最佳时机 III

题目链接:买卖股票的最佳时机 III - 困难

思路

买卖股票3,最多只能买卖2次。

  • dp[i]

    • dp[i] [0]:不操作 (可以不要,就为0)
    • dp[i] [1]:第一次持有
    • dp[i] [2]:第一次不持有
    • dp[i] [3]:第二次持有
    • dp[i] [4]:第二次不持有
  • 递推公式:

    • dp[i][0] = dp[i-1][0] 第i次不操作,由前一次不操作得出 (也就是0)
    • dp[i][1] = max(dp[i-1][1], dp[i-1][0] - peice[i]) 第一次持有 = i-1天持有 或 第一次买入(i-1天不操作)
    • dp[i][2] = max(dp[i-1][2], dp[i-1][1] + price[i]) 第一次不持有 = i-1天不持有 或 i-1天的时候卖出
    • dp[i][3] = max(dp[i-1][3], dp[i-1][2] - peice[i]) 第二次持有 = i-1天持有 或 i-1天买入
    • dp[i][4] = max(dp[i-1][4], dp[i-1][3] + price[i]) 第二次不持有 = i-1天卖出 或 i-1天卖出
  • 初始化:

    • dp[0][0] = 0
    • dp[0][1] = -price[i]
    • dp[0][2] = 0 第一次买入之后又卖出
    • dp[0][3] = -price[i] 第一次买入又卖出,第二次买入
    • dp[0][4] = 0 第一次买入又卖出,然后第二次买入又卖出
  • 遍历顺序:1 <= i < price.length

  • 打印dp

  • 结果

    • max(dp[price.length-1] [2], dp[price.length-1] [4]):取两次不持有时的最大值
    • dp[price.length-1] [4]:其实就是第二次不持有时的最大值。如果第一次不持有就是最大,我第二次就在当天买入又卖出得到利润0,也是第一次不持有的最大值。
class Solution {
    public int maxProfit(int[] prices) {
        int[][] dp = new int[prices.length][4];
        // 初始化
        dp[0][0] = -prices[0]; // 第一次买入
        dp[0][1] = 0; // 第一次卖出
        dp[0][2] = -prices[0]; // 第二次买入
        dp[0][3] = 0; // 第二次卖出for (int i = 1; i < prices.length; i++) {
            dp[i][0] = Math.max(dp[i-1][0], -prices[i]);
            dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0]+prices[i]);
            dp[i][2] = Math.max(dp[i-1][2], dp[i-1][1]-prices[i]);
            dp[i][3] = Math.max(dp[i-1][3], dp[i-1][2]+prices[i]);
//            System.out.printf("dp[%d][0]=%d  dp[%d][1]=%d  dp[%d][2]=%d  dp[%d][3]=%d\n",i,dp[i][0],i,dp[i][1],i,dp[i][2],i,dp[i][3]);
        }
        return dp[prices.length-1][3];
    }
}

打印结果:

Your input:[3,3,5,0,0,3,1,4]
Output:6
Expected:6
stdout:  从第二天开始
    第一次买入    第一次卖出    第二次买入     第二次卖出
    dp[1][0]=-3  dp[1][1]=0  dp[1][2]=-3  dp[1][3]=0  
    dp[2][0]=-3  dp[2][1]=2  dp[2][2]=-3  dp[2][3]=2
    dp[3][0]=0  dp[3][1]=2  dp[3][2]=2  dp[3][3]=2
    dp[4][0]=0  dp[4][1]=2  dp[4][2]=2  dp[4][3]=2
    dp[5][0]=0  dp[5][1]=3  dp[5][2]=2  dp[5][3]=5
    dp[6][0]=0  dp[6][1]=3  dp[6][2]=2  dp[6][3]=5
    dp[7][0]=0  dp[7][1]=4  dp[7][2]=2  dp[7][3]=6

总结

可以观察到第i的状态都是由i-1天的状态推导而出的,所以我们可以只用一个一维的dp数组来表示

class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[4];
        // 初始化
        dp[0] = -prices[0]; // 第一次买入
        dp[1] = 0; // 第一次卖出
        dp[2] = -prices[0]; // 第二次买入
        dp[3] = 0; // 第二次卖出for (int i = 1; i < prices.length; i++) {
            dp[0] = Math.max(dp[0], -prices[i]);
            dp[1] = Math.max(dp[1], dp[0]+prices[i]);
            dp[2] = Math.max(dp[2], dp[1]-prices[i]);
            dp[3] = Math.max(dp[3], dp[2]+prices[i]);
//            System.out.printf("dp[0]=%d  dp[1]=%d  dp[2]=%d  dp[3]=%d\n",dp[0],dp[1],dp[2],dp[3]);
        }
        return dp[3];
    }
}

打印dp:

Your input:[3,3,5,0,0,3,1,4]
Output:6
Expected:6
stdout:
    dp[0]=-3  dp[1]=0  dp[2]=-3  dp[3]=0
    dp[0]=-3  dp[1]=2  dp[2]=-3  dp[3]=2
    dp[0]=0  dp[1]=2  dp[2]=2  dp[3]=2
    dp[0]=0  dp[1]=2  dp[2]=2  dp[3]=2
    dp[0]=0  dp[1]=3  dp[2]=2  dp[3]=5
    dp[0]=0  dp[1]=3  dp[2]=2  dp[3]=5
    dp[0]=0  dp[1]=4  dp[2]=2  dp[3]=6

买卖股票的最佳时机IV LeetCode 188

题目链接:买卖股票的最佳时机IV LeetCode 188 - 困难

思路

从买卖股票3推断出dp[i]的状态在奇数次为买入,偶数次为卖出。

  • dp[i] [j]:i 第i支股票,j 买入或者卖出(是否持有)所获得的最大利润

  • 递推公式:从买卖股票3可以推导出

    • dp[0][0]= 0 0位表示无状态(可省略)

    • dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j] - price[i])

      • j+1为奇数,相当于持有 = i-1天持有 或者 i-1天不持有+买入
    • dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1] + price[i])

      • j+2为偶数,相当于不持有 = i-1天不持有 或者 i-1天持有+卖出
  • 初始化:同理

    • dp[0][j+1] = -price[i] 奇数次为买入
    • dp[0][j+2] = 0] 偶数次卖出,默认初始化为0
  • 遍历顺序: 1<=i

  • 打印dp

class Solution {
    public int maxProfit(int k, int[] prices) {
        int[][] dp = new int[prices.length][2*k + 1];
        // 初始化
        // 奇数次表示持有(买入),n次买入表示前面买卖钱花完了,所以都是prices[0]
        // j+2控制每一次为奇数  偶数次控制不持有(卖出),默认为0
        for (int j = 1; j < 2*k; j+=2) {
            dp[0][j] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            // j+=2,一组一组的表示每一次持有或者不持有的状态
            for (int j = 0; j < 2*k; j+=2) { // j最多能到2k-2,然后加2等于2k(即最后一位)
                // 持有:前一天持有 或者 前一天持有j+1-1 加买入-prices[i]
                dp[i][j+1] = Math.max(dp[i-1][j+1], dp[i-1][j] - prices[i]);
                // 不持有:前一天不持有 或者 前一天不持有j+2-1 加卖出+prices[i]
                dp[i][j+2] = Math.max(dp[i-1][j+2], dp[i-1][j+1] + prices[i]);
            }
        }
        return dp[prices.length-1][2*k];
    }
}

打印dp:

Your input:2
        [3,2,6,5,0,3]
Output:7
Expected:7
stdout:
    第1次持有    第1次不持有    第2次持有    第2次不持有
    dp[1][1]=-2 dp[1][2]=0  dp[1][3]=-2 dp[1][4]=0  
    dp[2][1]=-2 dp[2][2]=4  dp[2][3]=-2 dp[2][4]=4  
    dp[3][1]=-2 dp[3][2]=4  dp[3][3]=-1 dp[3][4]=4  
    dp[4][1]=0  dp[4][2]=4  dp[4][3]=4  dp[4][4]=4  
    dp[5][1]=0  dp[5][2]=4  dp[5][3]=4  dp[5][4]=7  

总结

买卖股票4是在买卖股票3的基础上,通过找规律得出的结果,相当于一个通式。

从地推公式第i天的状态完全是由i-1天的状态推导而出,所以我们可以写出一维数组的形式:

class Solution {
    public int maxProfit(int k, int[] prices) {
        int[] dp = new int[2*k + 1];
        // 初始化
        // 奇数次表示持有(买入),n次买入表示前面买卖钱花完了,所以都是prices[0]
        // j+2控制每一次为奇数  偶数次控制不持有(卖出),默认为0
        for (int j = 1; j < 2*k; j+=2) {
            dp[j] = -prices[0];
        }
        for (int i = 1; i < prices.length; i++) {
            // j+=2,一组一组的表示每一次持有或者不持有的状态
            for (int j = 0; j < 2*k; j+=2) {
                // 持有:前一天持有 或者 前一天持有j+1-1 加买入-prices[i]
                dp[j+1] = Math.max(dp[j+1], dp[j] - prices[i]);
                // 不持有:前一天不持有 或者 前一天不持有j+2-1 加卖出+prices[i]
                dp[j+2] = Math.max(dp[j+2], dp[j+1] + prices[i]);//                System.out.printf("dp[%d]=%d\t",j+1,dp[j+1]);
//                System.out.printf("dp[%d]=%d\t",j+2,dp[j+2]);
            }
//            System.out.println();
        }
        return dp[2*k];
    }
}

打印dp:

Your input:2
        [3,2,6,5,0,3]
Output:7
Expected:7
stdout:
    第1次持有    第1次不持有   第2次持有     第2次不持有
    dp[1]=-2    dp[2]=0     dp[3]=-2    dp[4]=0 
    dp[1]=-2    dp[2]=4     dp[3]=-2    dp[4]=4 
    dp[1]=-2    dp[2]=4     dp[3]=-1    dp[4]=4 
    dp[1]=0     dp[2]=4     dp[3]=4     dp[4]=4 
    dp[1]=0     dp[2]=4     dp[3]=4     dp[4]=7 

你可能感兴趣的:(代码训练营,算法,动态规划,leetcode,数据结构,java)