前两篇文章总结了力扣中一些典型的背包问题,着实比较耗费脑子,理解起来也需要一些时间,今天来介绍一下力扣中另一个典型的动态规划问题,买卖股票问题
本来我是不想介绍这个专题的,因为相比背包问题太简单了,但是仔细研究这个问题后发现这类问题给我提供了一种全新的设计dp数组的思路,让我大受启发,因此我还是准备介绍一下。
还是动态规划的4步走:
- 设计dp数组
- 确定递推公式
- dp数组初始化
- 明确递归顺序
话不多说,直接看题:
乍一看似乎根本用不上动态规划的思路,但是看了后面的几种变种题就会发现后面的题只能用动态规划的方法来做,所以这里介绍动态规划的解法。
首先介绍一下状态机的解题思路:
什么是状态机?
学过计算机组成原理与数字逻辑设计或者数字电路的人应该对这个词不陌生,说白了就是一种状态的转移,在不同的阶段完成不同的动作,每个阶段之间维护一个转移信号,这里举一个cache状态机的例子:
IDLE --> TAG_CHK -->REFILL
IDLE:初始状态,表示未收到访存请求
TAG_CHK:当收到访存请求时,从IDLE状态转移到TAG_CHK状态,根据访存地址的index在RAM中的cache块中查找并匹配tag,若匹配成功,则根据offset取出数据。
REFILL:匹配不成功时,从TAG_CHK转移到REFILL中,向总线发起读/写数据/指令请求
那么关于本题的状态机可以定义如下:
状态1:未购买股票前的阶段
状态2:持有股票期间
注意:这里的持有股票不是指恰好卖出股票,而是表示一种持有股票的长期状态(不一定恰好在本节点买入股票),其实也可以定义成两种状态,恰好买入股票和恰好持有股票,也能把题目做出来,首先在本题没必要,所以不定义,但是建议感兴趣的尝试一下,其次关于恰好卖出股票的节点,在后面的题会有更详细的说明
状态3:不持有股票期间
状态转移方程:未购买股票前->持有股票阶段->股票卖出阶段(不持有股票阶段)
状态机定义完了,下面我们开始动规
定义dp数组:这里我们定义一个二维数组dp[nums.size()][3],在遍历的时候我们按照一维遍历,更新二维的三个状态,dp[i][j]表示的就是第i天第j状态可以获得的最大收益
明确递推公式:
对于dp[i][0],由于这期间是一直没有操作的,所以dp[i][0]=dp[i-1][0]
对于dp[i][1],只能由它本身和初始状态推导而来,那么我们为了求最大收益,取一个最大值
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i])
对于dp[i][2],只能由持有股票状态和本身推导而来,因此公式如下:
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i])
代码如下:
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>>dp(prices.size(),vector<int>(3,0));
//dp[i][0]表示该阶段持有股票所拥有的现金,dp[i][1]表示该阶段不持有股票所拥有的现金
dp[0][1]-=prices[0];
for(int i=1;i<prices.size();i++)
{
dp[i][0]=dp[i-1][0];
dp[i][1]=max(dp[i-1][1],dp[i-1][0]-prices[i]);//由于只买卖股票一次,所以如果买入则默认前面都有没买,所以是-prices[i]
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]);
}
//return max(dp[prices.size()-1][1],dp[prices.size()-1][2]);
return dp[prices.size()-1][2];
}
};
后面的题基本也都是这个套路,之后的讲解会专注于状态机的设计和递推公式的书写
下一题:
本题与上一题是同一个状态机,唯一的区别就在于可以从状态3,也就是未持有股票推导而来
,同时可以发现第一个状态在实际运算过程基本不起什么作用,所以删掉
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>>dp(prices.size(),vector<int>(3,0));
dp[0][1]=-prices[0];
for(int i=1;i<prices.size();i++)
{
dp[i][1]=max(dp[i-1][1],dp[i-1][2]-prices[i]);//可以从状态3推导而来
dp[i][2]=max(dp[i-1][2],dp[i-1][1]+prices[i]);
}
return dp[prices.size()-1][2];
}
};