力扣121-买卖股票的最佳时机(Java详细题解)
题目链接:121. 买卖股票的最佳时机 - 力扣(LeetCode)
前情提要:
因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。
dp五部曲。
1.确定dp数组和i下标的含义。
2.确定递推公式。
3.dp初始化。
4.确定dp的遍历顺序。
5.如果没有ac打印dp数组 利于debug。
每一个dp题目如果都用这五步分析清楚,那么这道题就能解出来了。
题目思路:
本题是买卖股票系列的一个开始,所以本题很重要,大家要重点掌握。
首先看看题目描述,本题要求我们买卖股票后的最大利润,买卖股票只能进行一次。
那我们就得考虑在什么时候买股票和什么时候卖股票对吧。
其实什么时候买什么时候卖是不好推出我们的所有状态的。
我们这里主要是将状态分为持股和不持股俩个状态。
注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态
持股的意思是我当前持有股票的状态,我可能是当天买的也可能之前就持股了。
不持股就是我当前不持有股票的状态,我可能是当天卖的也可能之前就是不持股。这样我们就可以把整个dp数组给推出来。
接下来我们用dp五部曲系统分析一下。
1.确定dp数组和i下标的含义。
dp[i] [0]指的就是下标为i天持股手上的最大现金。
dp[i] [1]指的就是下标为i天不持股手上的最大现金。
可能有人会疑问,那我第一次买入股票那现金不是负的嘛。
确实是负的,不过我们求的是最大的负的,也就是实数中最小的数。其实就是得到最小买入股票的价格。
2.确定递推公式。
下标为i天持股手上的最大现金的状态可以由俩个状态推出。
我当天持股可能我前一天也是持股的状态,也可能是我当天买入股票的状态。
dp[i] [0] = Math.max(dp[i - 1] [0],-prices[i]);
因为本题只能买卖一次,所以第一买肯定是负数 也就是 0 - prices[i];
下标为i天不持股手上的最大现金的状态也可以由俩个状态推出。
我当天不持股可能我前一天也是不持股的状态,也可能是我当天卖出股票的状态。
我当天卖出,那我的前一天肯定是持股的状态,那我就将前一天持股手上的最大现金加上我当天卖出的股票最大现金,也就是我的最大不持股的现金(所得的利润)。
dp[i] [1] = Math.max(dp[i - 1] [1],dp[i - 1] [0] + prices[i]);
3.dp初始化。
该题可以从dp定义和递推公式来进行初始化。
可以看出dp[i]是由dp[i - 1]推出,所以需要前一个状态,那么我们所有状态的起始状态就是dp[0]
所以我们对dp[0]进行初始化。
dp[0] [0]是表示我i下标为0持股的状态,那肯定是当天买入股票的状态,因为他前面没有状态了,所以dp[0] [0] = -prices[0]
dp[0] [1]是表示我i下标为0不持股的状态,那就是我当天买当天买了,那手上的最大现金就为0了。所以dp[0] [1] = 0;
4.确定dp的遍历顺序。
可以看出dp[i]是由dp[i - 1]推出,所以需要前一个状态,那么我们的遍历顺序肯定是从前往后来推。
5.如果没有ac打印dp数组 利于debug。
最终代码:
class Solution {
public int maxProfit(int[] prices) {
//定义dp数组 表示第i天持股的最大现金(其实就是得到最小买入股票的价格,因为买入股票剩下的现金都为负数,在负数中最大就是实数中最小的数) 和 第i天不持股的最大现金(其实就是得到最大买出股票的价格)
//dp的定义
int [][] dp = new int [prices.length + 1][2];
//dp初始化
dp[0][0] = -prices[0];
dp[0][1] = 0;
//dp遍历顺序
for(int i = 1;i < prices.length;i ++){
//dp递推公式
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[prices.length - 1][1]
//在本题持股的最大现金一定为负数,所以得的最大值就是不持股的最大现金
return dp[prices.length - 1][1];
}
}
这一篇博客就到这了,如果你有什么疑问和想法可以打在评论区,或者私信我。
我很乐意为你解答。那么我们下篇再见!