121、★★股票问题Ⅳ-LeetCode-188.买卖股票的最佳时机Ⅳ

题目

给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iv

思路

不管使用几维数组,重要的是:

①将多少种状态分析情况;

②将状态表示完整;

③状态的转移关系弄清楚!

动态规划的重点

★理解:第几次交易的概念!

第一次交易:需要买了又卖了!并不是第一次没有买,这属于第0次交易!

分清楚:买在卖之前;完成一次买和卖,才算是完成了一次交易!

代码

1.三维数组解决问题

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0) return 0;
        //三种解答方式:一维、二维、三维,都是为了将每天 + k次交易的所有状态情况表示出来
        //①三维数组
        int len = prices.length;
        //这里交易数k的数组维度设置为 k+1表示,对边界情况好处理
        int[][][] dp = new int[len][k + 1][2];//len表示天数;k表示交易次数;2表示此时手里有没有股票
        //初始化,设置的是第一天时,出现不同状态的情况
        for(int i = 0;i < k + 1;i++){
            dp[0][i][0] = 0;//手里不持有股票
            dp[0][i][1] = -prices[0];
        }

        for(int i = 1;i < len;i++){
            for(int j = 1;j < k + 1;j++){
                dp[i][j][1] = Math.max(dp[i - 1][j][1],dp[i - 1][j - 1][0] - prices[i]);//第j次交易:此时是买入;只和前一天第j次买入 或者 前一天第j-1次交易卖出有关!
                dp[i][j][0] = Math.max(dp[i - 1][j][0],dp[i - 1][j][1] + prices[i]);//第j次交易:此时已经卖出:只和前一天第j次交易卖出 或者 前一天第j次交易买入有关!
            }
        }
        return dp[len - 1][k][0];
    }
}

2.二维数组解决问题

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0 || k== 0) return 0;
        int len = prices.length;

        //三种解答方式:一维、二维、三维,都是为了将每天 + k次交易的所有状态情况表示出来

        //②二维数组:考虑到状态转移方程中,dp[i]都是和前一天的dp[i - 1]有关,可以将维度降1
        int[][] dp = new int[k + 1][2];
        //这表示的是第一天的情况
        for(int i = 1;i < k + 1;i++){
            dp[i][1] = -prices[0];//1代表第k次买了
            dp[i][0] = 0; //0代表第k次没买
        }

        for(int i = 1;i <= len;i++){
            //第一个下标代表,交易的次数
            //此处和上一天是有关系的!看从哪一天开始!
            //这里的数值
            dp[1][1] = Math.max(dp[1][1],-prices[i - 1]);//之前就有,或者现在这天的价钱更低!
            dp[1][0] = Math.max(dp[1][0],dp[1][1] + prices[i -1]);//保持之前就没有的状态;或者之前有,卖掉了
            for(int j = 2;j < k + 1;j++){
                dp[j][1] = Math.max(dp[j][1],dp[j - 1][0] - prices[i - 1]);
                dp[j][0] = Math.max(dp[j][0],dp[j][1] + prices[i - 1]);
            }
        }
        return dp[k][0];
    }
}

想清楚买和卖的关系;以及和上一次交易、前一天情况的联系,可以更清楚!对2的改进

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0 || k== 0) return 0;
        int len = prices.length;

        //②二维数组:考虑到状态转移方程中,dp[i]都是和前一天的dp[i - 1]有关,可以将维度降1
        int[][] dp = new int[k + 1][2];
        //这表示的是 第一天 的情况:初始化
        for(int i = 1;i < k + 1;i++){
            dp[i][1] = -prices[0];//1代表第k次买了
            dp[i][0] = 0; //0代表第k次没买
        }
        //注意:第0次交易的情况暗含在里面:都是0
        for(int i = 1;i < len;i++){
            //从第一次交易,开始赋值!
            for(int j = 1;j < k + 1;j++){
                //1是买
                dp[j][1] = Math.max(dp[j][1],dp[j - 1][0] - prices[i]);
                //0是买了又卖了
                dp[j][0] = Math.max(dp[j][0],dp[j][1] + prices[i]);
            }
        }
        return dp[k][0];
    }
}

3.两个一维数组

class Solution {
    public int maxProfit(int k, int[] prices) {
        if(prices == null || prices.length == 0 || k== 0) return 0;
        int len = prices.length;

        //两个一维数组
        //将数组长度设置为 k + 1,因为边界条件的考虑,需要由交易0次的情况
        int[] buy = new int[k + 1];
        int[] sell = new int[k + 1];

        //第一天只有一支股票时的情况,初始化
        for(int i = 1;i <= k;i++){
            buy[i] = -prices[0];
        }
        for(int i = 1;i < len;i++){
            for(int j = 1;j <= k;j++){
                //保持前一天,当前交易次数时的情况,和前一天 前一次交易卖出了,当前又买的情况
                buy[j] = Math.max(buy[j],sell[j - 1] - prices[i]);
                sell[j] = Math.max(sell[j],buy[j] + prices[i]);
            }
        }
        return sell[k];
     }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值