DAY52:动态规划(十六)买卖股票最佳时机含冷冻期/手续费+股票系列总结

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

  • 本题是加了一个冷冻期,状态就多了,有点难度,大家要把各个状态分清,思路才能清晰

  • 股票系列难度:冷冻期>K次交易>两次交易>手续费,重点是理解状态转移

  • K次交易是仔细分析状态转移冷冻期是分清楚每个状态,对普通状态进行拆分两次交易是K次交易简化版,也是要分清楚交易状态手续费只是在无限次交易基础上,加上了每笔交易结束之后要付钱

给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。

设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):

  • 卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。

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

示例 1:

输入: prices = [1,2,3,0,2]
输出: 3 
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

示例 2:

输入: prices = [1]
输出: 0

提示:

  • 1 <= prices.length <= 5000
  • 0 <= prices[i] <= 1000

思路

本题和Ⅱ有点像,也是无限次买卖股票,但是加上了冷冻期。针对冷冻期,我们也需要列出来每一天的状态,分析每一天状态的关系。

注意,本题的冷冻期是仅仅当天卖出之后才会有第二天冷冻期冷冻期过了才能重新买入,因此冷冻期一定是不持有股票的状态!

之前的题目里,不持有股票的状态分为之前就不持有当天刚刚卖出。但是本题,这两种状态需要做拆分,因为当天刚刚卖出的情况会引起冷冻期

也就是说,当天卖出股票,跟随的下一天就是冷冻期,也就是下一个状态是冷冻期!

DP数组含义

  • dp[i][0]持有股票的状态
  • dp[i][1]不持有股票,之前就不持有的状态(注意拆分之后,之前就不持有的状态就不能加上刚刚卖出了,但是这种情况又包含了过了冷冻期的情况
  • dp[i][2]不持有股票,当天刚刚卖出的状态
  • dp[i][3]冷冻期状态(持续一天,第i天是冷冻期)

递推公式

dp[i][0]:持有股票状态,前一天就持有或者今天刚刚买入,今天刚刚买入可能是之前不持有买入,或者是过了冷冻期买入,也就是可能从两个状态转移过来

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

dp[i][1]:之前就不持有状态,已经拆分掉了今天刚刚卖的情况(在状态2),那么只可能是本来就不持有,或者是冷冻期刚过,也就是说只可能从状态1和状态3转移过来!

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

dp[i][2]:不持有股票,当天刚刚卖出状态,之前一定是有的,从状态0转移过来

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

dp[i][3]:冷冻期状态,一定是前一天刚刚卖出,从状态2转移过来,冷冻期不能操作

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

初始化

对i=0的情况进行初始化,也就是下标第i天的股票情况。

dp[0][0]=-prices[0];
dp[0][1]=0;
dp[0][2]=0;//刚刚卖了
dp[0][3]=0;//第一天买了+卖了,直接进入冷冻期

完整版

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.size()==1) return 0;
        vector<vector<int>>dp(prices.size(),vector<int>(4,0));
        //0这里初始化一定是-prices[0]而不是-prices[i],不能手误!
        dp[0][0]=-prices[0];//0:持有
        dp[0][1]=0;//1:保持不持有,可能是之前就不持有
        dp[0][2]=0;//2:当天卖了,引发第二天冷冻期
        dp[0][3]=0;//3:冷冻期
        for(int i=1;i<prices.size();i++){
            //0:持有股票状态,可能第一次买,也可能昨天是冷冻期
            dp[i][0]=max(dp[i-1][0],max(dp[i-1][1]-prices[i],dp[i-1][3]-prices[i]));
            //1:不持有
            dp[i][1]=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[size()-1]的一维数组里面找最大值
        return *max_element(dp[prices.size()-1].begin(),dp[prices.size()-1].end());

    }
};
注意Max的用法

遇到三个数字的比较的时候,必须要进行max的嵌套

总结

冷冻期这道题,关键是要想到拆分之前题目中的,不持有股票这个状态下的两种情况,即为本来就不持有当天刚刚卖出。因为当天刚刚卖出这种情况,会导致冷冻期。因此,当天刚刚卖出应该单独拆分为一个状态由该状态导致的冷冻期应该拆分为另一个状态

核心点还是理解状态转移的思想。

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

给定一个整数数组 prices,其中 prices[i]表示第 i 天的股票价格 ;整数 fee 代表了交易股票的手续费用。

你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。

**注意:**这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。

示例 1:

输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:  
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8

示例 2:

输入:prices = [1,3,7,5,10,3], fee = 3
输出:6

提示:

  • 1 <= prices.length <= 5 * 10^4
  • 1 <= prices[i] < 5 * 10^4
  • 0 <= fee < 5 * 10^4

思路

和冷冻期一样,本题同样和Ⅱ类似,都是无限次买卖股票。本题区别在于每笔交易都需要支付一笔手续费,支付手续费的时间可以在买入支付,也可以在卖出支付,每笔交易只需要支付一次

本题并不需要额外划分状态,只需要持有股票不持有股票两种即可。

DP数组含义

dp[i][0]持有股票的状态,由本来就持有状态和当天刚刚买入状态得到。

dp[i][1]不持有股票的状态,由本来就不持有状态和当天刚刚卖出状态得到(卖出减去手续费

递推公式

持有股票状态:

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

初始化

本题的初始化也是初始化dp[0][0]dp[0][1],因为只有0和1两种状态。

vector<vector<int>>dp(prices.size(),vector<int>(2,0));
dp[0][0]=-prices[0];//只有持有状态需要变化

完整版

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        vector<vector<int>>dp(prices.size(),vector<int>(2,0));
        dp[0][0]=-prices[0];
        for(int i=1;i<prices.size();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]-fee);
        }
        return dp[prices.size()-1][1];
    }
};

总结

本题和买卖股票Ⅱ很像,多了手续费,手续费是每次交易只需要一次,因此交易结束的时候上交。

股票系列总结:

  • 股票系列难度:冷冻期>K次交易>两次交易>手续费,重点是理解状态转移
  • K次交易是仔细分析状态转移冷冻期是分清楚每个状态,对普通状态进行拆分两次交易是K次交易简化版,也是要分清楚交易状态手续费只是在无限次交易基础上,加上了每笔交易结束之后要付钱
这个函数的作用是获取当前系统时间并格式化输出。其中,`date1` 是通过 `new Date()` 构造函数创建的一个 Date 对象,里面包了当前系统时间。然后通过 `getFullYear()`、`getMonth()`、`getDate()`、`getHours()`、`getMinutes()`、`getSeconds()` 方法分别获取年、月、日、小时、分钟、秒的值,并将其赋值给对应的变量 `year`、`month`、`day`、`hou`、`min`、`sec`。在获取到这些值之后,接下来就可以对它们进行格式化输出了。 如果您需要将这个函数修改为定时输出当前系统时间,可以使用 `setInterval` 函数来每隔一定时间执行一次 `getTime` 函数,代码示例如下: ```javascript function getTime() { var time = ""; // 先去获取系统时间 var date1 = new Date(); // 格式化时间 var year = date1.getFullYear(); // 得到整数 var month = date1.getMonth() + 1; // 修改+0 var day = date1.getDate(); // 修改+0 var hou = date1.getHours(); // 修改+0 var min = date1.getMinutes(); // 修改+0 var sec = date1.getSeconds(); // 修改+0 // 格式化输出 time += year + "年"; time += month < 10 ? "0" + month + "月" : month + "月"; time += day < 10 ? "0" + day + "日 " : day + "日 "; time += hou < 10 ? "0" + hou + ":" : hou + ":"; time += min < 10 ? "0" + min + ":" : min + ":"; time += sec < 10 ? "0" + sec : sec; console.log(time); } // 每隔1秒输出一次当前时间 setInterval(getTime, 1000); ``` 这个程序使用了 `setInterval` 函数来每隔1秒钟执行一次 `getTime` 函数,然后在 `getTime` 函数中对获取到的时间进行格式化输出。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值