Day43——Dp专题

news2024/9/25 19:24:05

文章目录

    • 股票问题篇
      • 21、买卖股票的最佳时机
      • 22、买卖股票的最佳时机II
      • 23、买卖股票的最佳时机Ⅲ
      • 24、买卖股票的最佳时机Ⅳ
      • 25、最佳买卖股票时机含冷冻期
      • 26、买卖股票的最佳时机含手续费
    • 股票问题总结篇


股票问题篇

21、买卖股票的最佳时机

力扣题目链接

动态规划

定义二维数组:dp[n][2]

状态表示

dp[i][0] 表示第i天没有持有股票所得最大现金(利润)

dp[i][1] 表示第i天持有股票所得最大现金(利润)

状态计算

  • dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]

  • dp[i][1] = max(dp[i - 1][1], -prices[i])

    • i-1天就持有股票,那么就保持现状(不买入),所得现金就是昨天持有股票的所得现金 即:dp[i - 1][1]

    • i天买入股票(买入),所得现金就是买入今天的股票后所得现金即:-prices[i]

初始化

  • f[0][1] = -prices[0];
  • f[0][0] = 0;

结果返回:f[n - 1][0]:最后一天不持有股票的最大利润(要么买了股票然后卖出去赚钱了,要么没买即为0)

Code

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][1] = -prices[0];
        dp[0][0] = 0;
        for(int i = 1; i < n; i++){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],-prices[i]);
        }
        return dp[n - 1][0];
    }
}

22、买卖股票的最佳时机II

力扣题目链接

本题和题I的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)!

动态规划

定义二维数组:dp[n][2]

状态表示

dp[i][0] 表示第i天没有持有股票所得最大现金(利润)

dp[i][1] 表示第i天持有股票所得最大现金(利润)

状态计算

  • dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i] ——Ⅱ

  • dp[i][1] = max(dp[i - 1][1], dp[i - 1][0]-prices[i]) ——Ⅰ

    • i-1天就持有股票,那么就保持现状(不买入),所得现金就是昨天持有股票的所得现金 即:dp[i - 1][1]

    • i天买入股票(买入),所得现金就是买入今天的股票后所得现金即:dp[i - 1][0]-prices[i])

初始化

  • f[0][1] = -prices[0];
  • f[0][0] = 0;

结果返回:f[n - 1][0]:最后一天不持有股票的最大利润(要么买了股票然后卖出去赚钱了,要么没买即为0)

对比Ⅰ和Ⅱ买卖股票的最佳时机

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

dp[i][1] = max(dp[i - 1][1], -prices[i])

原因就是II可以买卖多次就要积累前面赚到的最大利润,而I只能买卖一次,刚刚买入时利润必定是-prices[i]

Code

二维数组

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][1] = -prices[0];
        dp[0][0] = 0;
        for(int i = 1; i < n; i++){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

一维数组

// 优化空间
class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[2];
        dp[1] = -prices[0];
        dp[0] = 0;
        for(int i = 1; i < prices.length; i++){
            dp[1] = Math.max(dp[1], dp[0] - prices[i]);
            dp[0] = Math.max(dp[0], dp[1] + prices[i]);
        }
        return dp[0];
    }
}

23、买卖股票的最佳时机Ⅲ

力扣题目链接

动规五部曲

  • 确定dp数组以及下标含义
  1. 没有操作
  2. 第一次买入
  3. 第一次卖出
  4. 第二次买入
  5. 第二次卖出

dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金

  • 确定递推公式

dp[i][1],表示的是第i天,买入股票的状态,并不是说一定要第i天买入股票,有可能i-1天买的股票

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

每次dp第一种是上一个状态的延续,还有一种为本次的改变(买入或卖出)

同理可推出剩下状态部分:

dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);

dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);

            dp[i][1] = Math.max(dp[i-1][1],-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]);
            dp[i][4] = Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
  • dp数组初始化

第0天没有操作,这个最容易想到,就是0,即:dp[0][0] = 0;

第0天做第一次买入的操作,dp[0][1] = -prices[0];

存在一种情况当天买,当天卖

  • 第一次持有或第二次持有
  dp[0][1] = -prices[0];
  dp[0][3] = -prices[0];
  • 第一次不持有或第二次不持有
dp[0][2] = 0;
dp[0][4] = 0;
  • 确定遍历顺序

从递归公式其实已经可以看出,一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。

  • 举例并推导dp数组

image-20221212220237134

现在最大的时候一定是卖出的状态,而两次卖出的状态现金最大一定是最后一次卖出。

所以最终最大利润是dp[n-1][4]

Code

二维

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(prices==null||n==0){
            return 0;
        }
        int[][] dp = new int[n][5];
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for(int i = 1; i < n; i++){
            dp[i][1] = Math.max(dp[i-1][1],-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]);
            dp[i][4] = Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[n-1][4];
    }
}

一维

class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[5]; 
        dp[1] = -prices[0];
        dp[3] = -prices[0];
        for(int i = 1; i < prices.length; i++){
            dp[1] = Math.max(dp[1], -prices[i]);
            dp[2] = Math.max(dp[2], dp[1]+prices[i]);
            dp[3] = Math.max(dp[3], dp[2]-prices[i]);
            dp[4] = Math.max(dp[4], dp[3]+ prices[i]);
        }
        return dp[4];
    }
}

24、买卖股票的最佳时机Ⅳ

力扣题目链接

本题与Ⅲ的区别在于买卖股票k次,就是将Ⅲ抽象出来

动规五部曲

  • 确定dp数组以及下标的含义

使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

j的状态表示为:

  • 0 表示不操作
  • 1 第一次买入
  • 2 第一次卖出
  • 3 第二次买入
  • 4 第二次卖出
  • j+1就是买入,j+2就是卖出

除了0以外,偶数就是卖出,奇数就是买入

  • 确定递推公式
  dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
  dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
  • dp数组初始化

dp[0][j]当j为奇数的时候都初始化为 -prices[0],j为偶数的时候都初始化为0

  • 确定遍历顺序

一定是从前向后遍历,因为dp[i],依靠dp[i - 1]的数值。

  • 举例并推导dp数组

image-20221214195453126

最后一次卖出,一定是利润最大的,dp[prices.size() - 1][2 * k]即红色部分就是最后求解

Code

二维:股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2*k+1];
        for(int j = 1;j < 2*k; j += 2){
            dp[0][j] = -prices[0];
        }
        for(int i = 1;i < n;i++){
            for(int j = 0;j < 2*k;j += 2){
                dp[i][j+1] = Math.max(dp[i-1][j+1],dp[i-1][j] - prices[i]);
                dp[i][j+2] = Math.max(dp[i-1][j+2],dp[i-1][j+1] + prices[i]);
            }
        }
        return dp[n - 1][2*k];
    }
}

一维(参考)

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0){
            return 0;
        }
        if(k == 0){
            return 0;
        }
        // 其实就是123题的扩展,123题只用记录2次交易的状态
        // 这里记录k次交易的状态就行了
        // 每次交易都有买入,卖出两个状态,所以要乘 2
        int[] dp = new int[2 * k];
        // 按123题解题格式那样,做一个初始化
        for(int i = 0; i < dp.length / 2; i++){
            dp[i * 2] = -prices[0];
        }
        for(int i = 1; i <= prices.length; i++){
            dp[0] = Math.max(dp[0], -prices[i - 1]);
            dp[1] = Math.max(dp[1], dp[0] + prices[i - 1]);
            // 还是与123题一样,与123题对照来看
            // 就很容易啦
            for(int j = 2; j < dp.length; j += 2){
                dp[j] = Math.max(dp[j], dp[j - 1] - prices[i-1]);
                dp[j + 1] = Math.max(dp[j + 1], dp[j] + prices[i - 1]);
            }
        }
        // 返回最后一次交易卖出状态的结果就行了
        return dp[dp.length - 1];
    }
}

25、最佳买卖股票时机含冷冻期

力扣题目链接

动规五部曲

  • 确定dp数组以及下标的含义

dp[i][j],第i天状态为j,所剩的最多现金为dp[i][j]

  • 确定递推公式

  • 状态一:买入股票状态(今天买入股票,或者是之前就买入了股票然后没有操作)dp[i][0]

  • 卖出股票状态,这里就有两种卖出股票状态

    • 状态二:两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态dp[i][1]
    • 状态三:今天卖出了股票dp[i][2]
  • 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!dp[i][3]

达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:

  • 操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]
  • 操作二:今天买入了,有两种情况
    • 前一天是冷冻期(状态四),dp[i - 1][3] - prices[i]
    • 前一天是保持卖出股票状态(状态二),dp[i - 1][1] - prices[i]

所以操作二取最大值,即:max(dp[i - 1][3], dp[i - 1][1]) - prices[i]

那么dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

达到保持卖出股票状态(状态二)即:dp[i][1],有两个具体操作:

  • 操作一:前一天就是状态二
  • 操作二:前一天是冷冻期(状态四)

dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);

达到今天就卖出股票状态(状态三),即:dp[i][2] ,只有一个操作:

  • 操作一:昨天一定是买入股票状态(状态一),今天卖出

即:dp[i][2] = dp[i - 1][0] + prices[i];

达到冷冻期状态(状态四),即:dp[i][3],只有一个操作:

  • 操作一:昨天卖出了股票(状态三)

dp[i][3] = dp[i - 1][2];

综上分析,递推代码如下:

 dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][1] - prices[i],dp[i-1][3] - prices[i]));
 dp[i][1] = Math.max(dp[i-1][1],dp[i-1][3]);
 dp[i][2] = (dp[i-1][0] + prices[i]);
 dp[i][3] = (dp[i-1][2]);
  • dp数组初始化

对于在定义中没有意义的数组,可通过递推公式确定

dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 0;
  • 确定遍历顺序

从递归公式上可以看出,dp[i] 依赖于 dp[i-1],所以是从前向后遍历。

  • 举例并推导dp数组

image-20221217111152412

取最大值

return Math.max(dp[n-1][1],Math.max(dp[n-1][2],dp[n-1][3]));

Code

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null|prices.length==0) return 0;
        int n = prices.length;
        int[][] dp = new int[n][4];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;
        dp[0][3] = 0;
        for(int i = 1;i < n;i++){
            dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][1] - prices[i],dp[i-1][3] - prices[i]));
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][3]);
            dp[i][2] = (dp[i-1][0] + prices[i]);
            dp[i][3] = (dp[i-1][2]);
        }
        return Math.max(dp[n-1][1],Math.max(dp[n-1][2],dp[n-1][3]));
    }
}

26、买卖股票的最佳时机含手续费

力扣题目链接

动规五部曲

  • 确定dp数组以及下标的含义

``dp[i][j]`表示第i天的状态为j,所剩的最多现金

  • 确定递推公式

dp[i][0] 表示第i天持有股票所剩最多现金。 dp[i][1] 表示第i天不持有股票所得最多现金

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:dp[i - 1][0] + prices[i] - fee
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] - prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i] - fee);
  • dp数组初始化
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
  • 确定遍历顺序

从递归公式上可以看出,dp[i] 依赖于 dp[i-1],所以是从前向后遍历。

Code

二维

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if(prices==null||prices.length==0) return 0;
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1;i < n;i++){
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] - prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i] - fee);
        }
        return dp[n-1][1];
    }
}

一维

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if(prices==null||prices.length==0) return 0;
        int n = prices.length;
        int[] dp = new int[2];
        dp[0] = -prices[0];
        dp[1] = 0;
        for(int i = 1;i < n;i++){
            dp[0] = Math.max(dp[0],dp[1] - prices[i]);
            dp[1] = Math.max(dp[1],dp[0] + prices[i] - fee);
        }
        return dp[1];
    }
}

股票问题总结篇

image-20221217115042848

卖股票的最佳时机

【动态规划】

  • dp[i][0] 表示第i天持有股票所得现金。
  • dp[i][1] 表示第i天不持有股票所得现金。

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是买入今天的股票后所得现金即:-prices[i] 所以dp[i][0] = max(dp[i - 1][0], -prices[i]);

如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0] 所以dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);

Code

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][1] = -prices[0];
        dp[0][0] = 0;
        for(int i = 1; i < n; i++){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],-prices[i]);
        }
        return dp[n - 1][0];
    }
}

买卖股票的最佳时机II

【动态规划】

dp数组定义:

  • dp[i][0] 表示第i天持有股票所得现金
  • dp[i][1] 表示第i天不持有股票所得最多现金

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

在121. 买卖股票的最佳时机中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。

而本题**,因为一只股票可以买卖多次**,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。

Code

二维数组

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][1] = -prices[0];
        dp[0][0] = 0;
        for(int i = 1; i < n; i++){
            dp[i][0] = Math.max(dp[i - 1][0],dp[i - 1][1] + prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1],dp[i - 1][0] - prices[i]);
        }
        return dp[n - 1][0];
    }
}

一维数组

// 优化空间
class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[2];
        dp[1] = -prices[0];
        dp[0] = 0;
        for(int i = 1; i < prices.length; i++){
            dp[1] = Math.max(dp[1], dp[0] - prices[i]);
            dp[0] = Math.max(dp[0], dp[1] + prices[i]);
        }
        return dp[0];
    }
}

买卖股票的最佳时机Ⅲ

【动态规划】

一天一共就有五个状态,

  1. 没有操作
  2. 第一次买入
  3. 第一次卖出
  4. 第二次买入
  5. 第二次卖出

dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i-1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

所以dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

同理可推出剩下状态部分:

dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);

dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);

Code

二维

class Solution {
    public int maxProfit(int[] prices) {
        int n = prices.length;
        if(prices==null||n==0){
            return 0;
        }
        int[][] dp = new int[n][5];
        dp[0][1] = -prices[0];
        dp[0][3] = -prices[0];
        for(int i = 1; i < n; i++){
            dp[i][1] = Math.max(dp[i-1][1],-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]);
            dp[i][4] = Math.max(dp[i-1][4],dp[i-1][3]+prices[i]);
        }
        return dp[n-1][4];
    }
}

一维

class Solution {
    public int maxProfit(int[] prices) {
        int[] dp = new int[5]; 
        dp[1] = -prices[0];
        dp[3] = -prices[0];
        for(int i = 1; i < prices.length; i++){
            dp[1] = Math.max(dp[1], -prices[i]);
            dp[2] = Math.max(dp[2], dp[1]+prices[i]);
            dp[3] = Math.max(dp[3], dp[2]-prices[i]);
            dp[4] = Math.max(dp[4], dp[3]+ prices[i]);
        }
        return dp[4];
    }
}

买卖股票的最佳时机Ⅳ

使用二维数组 dp[i][j] :第i天的状态为j,所剩下的最大现金是dp[i][j]

j的状态表示为:

  • 0 表示不操作
  • 1 第一次买入
  • 2 第一次卖出
  • 3 第二次买入
  • 4 第二次卖出

除了0以外,偶数就是卖出,奇数就是买入

  1. 确定递推公式

达到dp[i][1]状态,有两个具体操作:

  • 操作一:第i天买入股票了,那么dp[i][1] = dp[i - 1][0] - prices[i]
  • 操作二:第i天没有操作,而是沿用前一天买入的状态,即:dp[i][1] = dp[i - 1][1]

dp[i][1] = max(dp[i - 1][0] - prices[i], dp[i - 1][1]);

同理dp[i][2]也有两个操作:

  • 操作一:第i天卖出股票了,那么dp[i][2] = dp[i - 1][1] + prices[i]
  • 操作二:第i天没有操作,沿用前一天卖出股票的状态,即:dp[i][2] = dp[i - 1][2]

dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

Code

二维:股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices==null||prices.length==0){
            return 0;
        }
        int n = prices.length;
        int[][] dp = new int[n][2*k+1];
        for(int j = 1;j < 2*k; j += 2){
            dp[0][j] = -prices[0];
        }
        for(int i = 1;i < n;i++){
            for(int j = 0;j < 2*k;j += 2){
                dp[i][j+1] = Math.max(dp[i-1][j+1],dp[i-1][j] - prices[i]);
                dp[i][j+2] = Math.max(dp[i-1][j+2],dp[i-1][j+1] + prices[i]);
            }
        }
        return dp[n - 1][2*k];
    }
}

一维(参考)

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices.length == 0){
            return 0;
        }
        if(k == 0){
            return 0;
        }
        // 其实就是123题的扩展,123题只用记录2次交易的状态
        // 这里记录k次交易的状态就行了
        // 每次交易都有买入,卖出两个状态,所以要乘 2
        int[] dp = new int[2 * k];
        // 按123题解题格式那样,做一个初始化
        for(int i = 0; i < dp.length / 2; i++){
            dp[i * 2] = -prices[0];
        }
        for(int i = 1; i <= prices.length; i++){
            dp[0] = Math.max(dp[0], -prices[i - 1]);
            dp[1] = Math.max(dp[1], dp[0] + prices[i - 1]);
            // 还是与123题一样,与123题对照来看
            // 就很容易啦
            for(int j = 2; j < dp.length; j += 2){
                dp[j] = Math.max(dp[j], dp[j - 1] - prices[i-1]);
                dp[j + 1] = Math.max(dp[j + 1], dp[j] + prices[i - 1]);
            }
        }
        // 返回最后一次交易卖出状态的结果就行了
        return dp[dp.length - 1];
    }
}

最佳买卖股票时机含冷冻期

  • dp[i][j],第i天状态为j,所剩的最多现金为dp[i][j]

  • 状态一:买入股票状态(今天买入股票,或者是之前就买入了股票然后没有操作)dp[i][0]

  • 卖出股票状态,这里就有两种卖出股票状态

    • 状态二:两天前就卖出了股票,度过了冷冻期,一直没操作,今天保持卖出股票状态dp[i][1]
    • 状态三:今天卖出了股票dp[i][2]
  • 状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!dp[i][3]

达到买入股票状态(状态一)即:dp[i][0],有两个具体操作:

  • 操作一:前一天就是持有股票状态(状态一),dp[i][0] = dp[i - 1][0]
  • 操作二:今天买入了,有两种情况
    • 前一天是冷冻期(状态四),dp[i - 1][3] - prices[i]
    • 前一天是保持卖出股票状态(状态二),dp[i - 1][1] - prices[i]

所以操作二取最大值,即:max(dp[i - 1][3], dp[i - 1][1]) - prices[i]

那么dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

达到保持卖出股票状态(状态二)即:dp[i][1],有两个具体操作:

  • 操作一:前一天就是状态二
  • 操作二:前一天是冷冻期(状态四)

dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);

达到今天就卖出股票状态(状态三),即:dp[i][2] ,只有一个操作:

  • 操作一:昨天一定是买入股票状态(状态一),今天卖出

即:dp[i][2] = dp[i - 1][0] + prices[i];

达到冷冻期状态(状态四),即:dp[i][3],只有一个操作:

  • 操作一:昨天卖出了股票(状态三)

dp[i][3] = dp[i - 1][2];

综上分析,递推代码如下:

 dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][1] - prices[i],dp[i-1][3] - prices[i]));
 dp[i][1] = Math.max(dp[i-1][1],dp[i-1][3]);
 dp[i][2] = (dp[i-1][0] + prices[i]);
 dp[i][3] = (dp[i-1][2]);

Code

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null|prices.length==0) return 0;
        int n = prices.length;
        int[][] dp = new int[n][4];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        dp[0][2] = 0;
        dp[0][3] = 0;
        for(int i = 1;i < n;i++){
            dp[i][0] = Math.max(dp[i-1][0],Math.max(dp[i-1][1] - prices[i],dp[i-1][3] - prices[i]));
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][3]);
            dp[i][2] = (dp[i-1][0] + prices[i]);
            dp[i][3] = (dp[i-1][2]);
        }
        return Math.max(dp[n-1][1],Math.max(dp[n-1][2],dp[n-1][3]));
    }
}

买卖股票的最佳时机含手续费

  • dp[i][j]表示第i天的状态为j,所剩的最多现金

  • dp[i][0] 表示第i天持有股票所剩最多现金。 dp[i][1] 表示第i天不持有股票所得最多现金,如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]

  • 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出后所得现金即:dp[i - 1][0] + prices[i] - fee
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] - prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i] - fee);

Code

二维

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if(prices==null||prices.length==0) return 0;
        int n = prices.length;
        int[][] dp = new int[n][2];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for(int i = 1;i < n;i++){
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] - prices[i]);
            dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] + prices[i] - fee);
        }
        return dp[n-1][1];
    }
}

一维

class Solution {
    public int maxProfit(int[] prices, int fee) {
        if(prices==null||prices.length==0) return 0;
        int n = prices.length;
        int[] dp = new int[2];
        dp[0] = -prices[0];
        dp[1] = 0;
        for(int i = 1;i < n;i++){
            dp[0] = Math.max(dp[0],dp[1] - prices[i]);
            dp[1] = Math.max(dp[1],dp[0] + prices[i] - fee);
        }
        return dp[1];
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/97642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【GRU回归预测】基于matlab粒子群算法优化门控循环单元PSO-GRU神经网络回归预测(多输入单输出)【含Matlab源码 2286期】

⛄一、CNN-GRU数据预测 1 理论基础 1.1 CNN算法 负荷序列数据为一维数据&#xff0c;用一维卷积核对数据进行卷积处理&#xff0c;以获取数据的特征。 现设定卷积核的维度为3&#xff0c;移动步长为1&#xff0c;对输入数据进行卷积&#xff0c;以获得特征图图谱&#xff0c;即…

WordCount 案例实操

文章目录WordCount 案例实操一、案例需求二、案例分析三、代码实现1、Map阶段代码WordCount 案例实操 一、案例需求 1、需求&#xff1a;在给定的文本文件中统计输出每一个单词的总次数 2、期望输出数据 atguigu 2 banzhang 1 cls 2 hadoop 1 jiao 1 ss 2 xue 1 二、案例分…

java和springboot开发祭祀小程序扫墓系统代祭小程序

介绍 绿色祭祀小程序&#xff0c;用户微信授权登录后可以发布纪念馆&#xff08;祭祀名人&#xff0c;祭祀英烈&#xff0c;祭祀个人&#xff0c;可以设置成公开状态或者隐私状态&#xff09;购买祭祀礼物和留言&#xff0c;用户还可以发布代祭的信息 演示视频 小程序 https:…

计算机毕业设计ssm+vue基本微信小程序的购物商城小程序

项目介绍 随着互联网的趋势的到来,各行各业都在考虑利用互联网将自己的信息推广出去,最好方式就是建立自己的平台信息,并对其进行管理,随着现在智能手机的普及,人们对于智能手机里面的应用购物平台小程序也在不断的使用,本文首先分析了购物平台小程序应用程序的需求,从系统开发…

Spark SQL执行计划到RDD全流程记录

目录 0、样例说明 1、解析词义&#xff0c;语义&#xff0c;生成语法树 1.1、概念 1.2、根据样例跟踪Spark代码 2、Unresolved Logical Plan 3、Analyzed Logical Plan 源码 SessionCatalog Analyzer Rule Batch 对比 4、Optimized Logical Plan 5、Physical Plan …

vue3组件基础,组件引用与使用、向子组件传递数据与事件prop、emit

vue3组件基础&#xff0c;组件引用与使用、向子组件传递数据与事件prop、emit 一、组件模板 组成&#xff1a;template(必要)&#xff0c;script&#xff0c;style 例子&#xff1a;模板名称 Hello.vue <template><div class"msgStyle">{{ msg }}</di…

Java项目:ssm校园在线点餐系统源码

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 系统主要分为前台和后台&#xff0c;分为管理员与普通用户两种角色&#xff1b; 前台主要功能有&#xff1a;用户注册、用户登录、我的购物车、…

大学生零基础学编程要多久呢

众所周知现在的程序员都是高薪工作&#xff0c;很多人会通过自学或培训去获得一些编程知识&#xff0c;那“学编程需要什么基础呢&#xff1f;” 编程培训已经成为了很多大学毕业生缓解就业压力的一种方式&#xff0c;毕业之后找一份高薪工作是许多大学生最基本想法&#xff0…

【学习打卡03】可解释机器学习笔记之CAM类激活热力图

可解释机器学习笔记之CAM类激活热力图 文章目录可解释机器学习笔记之CAM类激活热力图CAM介绍CAM算法原理GAP全局平均池化GAP VS GMPCAM算法的缺点及改进CAM可视化同张图&#xff0c;不同类别不同图&#xff0c;同个类别CAM弱监督定位用语义特征编码进行分类CAM各种有意思的应用…

Linux系统中DDR3硬件初始化实验

大家好&#xff0c;我是ST。 今天的话&#xff0c;主要和大家聊一聊&#xff0c;如何使用Cortex-A芯片自带的RAM&#xff0c;很多时候要运行Linux的话是完全不够用的&#xff0c;必须要外接一片RAM芯片&#xff0c;驱动开发板上的DDR3。 目录 第一&#xff1a;何为RAM和ROM …

为什么要学习Python爬虫与数据可视化?

提到Python爬虫与数据可视化&#xff0c;我们都不陌生。因为我们早已身在大数据驱动的时代&#xff0c;数据分析已然成为了一项必备技能。可能有人会问&#xff0c;为什么要学习Python爬虫与数据可视化&#xff1f; 答案是显而易见的&#xff0c;无论是出于时代发展的要求&…

redis之如何支持秒杀场景

写在前面 本文一起看下Redis在秒杀场景中的应用。 1&#xff1a;秒杀都有哪些阶段 redis并非在秒杀的所有阶段都需要使用到&#xff0c;为了更好的了解redis在秒杀场景中的应用&#xff0c;我们先来看下秒杀的不同阶段&#xff0c;基本可以分为秒杀前&#xff0c;秒杀进行时&…

什么是用户增长? (超详细)

一.概况 原因&#xff1a;随着人口红利的衰减&#xff0c;互联网流量红利的马太效应显现&#xff0c;这意味着成本的大幅度增加&#xff0c;企业必须改变过去粗放型的营销和运营方式&#xff0c;用更高效更低成本实现快速增长 定义&#xff1a;通过实验和数据驱动&#xff0c…

5.Linux实用操作

文章目录零、学习目标一、软件安装1、Linux系统的应用商店2、yum命令3、apt命令 - 扩展二、systemctl命令三、软连接四、下载和网络请求1、ping命令2、wget命令3、curl命令五、端口1、概念2、查看端口占用六、进程管理1、概念2、查看进程3、查看指定进程4、关闭进程七、主机状态…

SAP ABAP 开发管理 代码增强标记 位置使用清单(Mark of enhancement)

SAP ABAP 开发管理 代码增强标记 位置使用清单&#xff08;Mark of enhancement&#xff09; 引言&#xff1a; 代码增强标记 &#xff08;Mark of enhancement&#xff09;是我在 ABAP 开发中对增强管理的方法之一&#xff0c;是对 SAP 系统增强管理工具的补充。通过对代码增…

自学Python找不到工作?只要掌握这七大块,月薪15K轻轻松松

1. 开发环境和开发工具 python3.6下载 Download Python sublime Text 3 Sublime Text - Download pycharm下载 PyCharm :: Download Latest Version of PyCharm 2. python语法知识 个人推荐《Python从入门到实践》、《Python编程快速上手》 3. web框架 djangoh中文文档 D…

九、Docker 复杂安装之mysql主从复制

前面我们介绍了Docker 安装单机版mysql,如果没有看可以先看下:https://blog.csdn.net/u011837804/article/details/128315385 本篇学习的前提是懂得mysql主从复制的原理,话不多说,我们开始。 1、下载mysql5.7镜像 涉及命令: 查看本地镜像命令:docker images拉取mysql5…

最近邻 M 点

一 问题描述 在 K 维空间中有很多点&#xff0c;给定一个点&#xff0c;找出最近的 M 个点。点 p 和点 q 之间的距离是连接它们的直线段的长度。 二 输入和输出 1 输入 有多个测试用例。第 1 行包含两个非负整数 n 和 k &#xff0c;分别表示点数和维数&#xff0c;1≤n≤5…

Python: unittest框架

目录 1.0 接口自动化框架设计 2.0 分层设计框架 3.0 接口配置文件 3.1 测试用例数据 4.0 框架执行入口 4.1 测试函数 4.1.1 参数替换 4.1.2 发送请求处理 4.1.3 响应断言 4.1.4 提取全局变量 4.1.5 数据库断言 5.0 工具类 5.1.1 excel文件处理 5.1.2 数…

直呼内行阿里离职带出内网专属“高并发系统设计”学习笔记

前言 我们知道&#xff0c;高并发代表着大流量&#xff0c;高并发系统设计的魅力就在于我们能够凭借自己的聪明才智设计巧妙的方案&#xff0c;从而抵抗巨大流量的冲击&#xff0c;带给用户更好的使用体验。这些方案好似能操纵流量&#xff0c;让流量更加平稳得被系统中的服务…