「代码随想录算法训练营」第三十五天 | 动态规划 part8
121. 买卖股票的最佳时机
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
文章讲解:https://programmercarl.com/0121.买卖股票的最佳时机.html
题目难度:简单
视频讲解:https://www.bilibili.com/video/BV1Xe4y1u77q
题目状态:有一半的思路
思路一:贪心
对数组进行遍历,分别保存两个变量:最小的买入时间left
,最大的获利金额ans
,遍历完就得到答案了。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { int left = INT_MAX; int ans = 0; for(int i = 0; i < prices.size(); ++i) { left = min(left, prices[i]); ans = max(ans, prices[i] - left); } return ans; } };
思路二:动规
创建一个二维动规数组dp[i][]
,其大小为vector<len, vector<int>(2)>
。
dp[i][0]
表示在第i天持有所花费的钱,此时并不一定是在i天买入的,也可能是在i-1天买入的,这个就是其动规的公式:dp[i][0] = max(dp[i - 1][0], -price[i])
。dp[i][1]
表示在第i天整到的最多的钱,此时也并不一定必须在i天卖出,也可能是在i-1天卖出的,取决于谁获利最多,动规公式如下:dp[i][1] = max(price[i] + dp[i - 1][0], dp[i - 1][0])
。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); if(len == 0) return 0; vector<vector<int>> dp(len, vector<int>(2)); dp[0][0] = -prices[0]; dp[0][1] = 0; for(int i = 1; i < len; ++i) { dp[i][0] = max(dp[i - 1][0], -prices[i]); dp[i][1] = max(prices[i] + dp[i - 1][0], dp[i - 1][1]); } return dp[len - 1][1]; } };
思路二代码改进:
由于dp[i][]
只有dp[i - 1][]
来进行动规,因此只需要维持这两个数就行,不需要维护其他数,采用滑动窗口的形式,代码如下:
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); vector<vector<int>> dp(2, vector<int>(2)); // 注意这里只开辟了一个2 * 2大小的二维数组 dp[0][0] -= prices[0]; dp[0][1] = 0; for (int i = 1; i < len; i++) { dp[i % 2][0] = max(dp[(i - 1) % 2][0], -prices[i]); dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]); } return dp[(len - 1) % 2][1]; } };
122. 买卖股票的最佳时机 II
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/
文章讲解:https://programmercarl.com/0122.买卖股票的最佳时机II(动态规划).html
题目难度:中等
视频讲解:https://www.bilibili.com/video/BV1D24y1Q7Ls
题目状态:有一半的思路
思路一:贪心
这题在学习贪心算法的时候做过,只需要遍历数组,然后将具有正收益的盈利金额加起来。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { int res = 0; for(int i = 1; i < prices.size(); ++i) { res += max(prices[i] - prices[i - 1], 0); } return res; } };
思路二:动态规划
这里和上一题一样,也要维持一个二维动规数组,但有一个地方需要改变,就是在dp[i][0]
的计算上,上一题因为只能进行一次交易,所以dp[i][0]
要么是dp[i - 1][0]
(在第i天之前已经进行买入),要么是-price[i]
(在当天买入)。但这道题中,我们可以进行多次交易,因此dp[i][0]
要么是dp[i - 1][0]
(在第i天之前进行买入),要么是dp[i - 1][1] - price[i]
(在当天进行买入,我们要将之前的收益也要加进来)。
要始终记得:dp[i][0]
表示第i天持有股票的最大利润;dp[i][1]
表示第i天不持有股票的最大利润。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); if(len == 0) return 0; vector<vector<int>> dp(len, vector<int>(2)); dp[0][0] = -prices[0]; dp[0][1] = 0; for(int i = 1; i < len; ++i) { dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]); dp[i][1] = max(dp[i - 1][0] + prices[i], dp[i - 1][1]); } return dp[len - 1][1]; } };
滚动数组版本:
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); vector<vector<int>> dp(2, vector<int>(2)); // 注意这里只开辟了一个2 * 2大小的二维数组 dp[0][0] -= prices[0]; dp[0][1] = 0; for (int i = 1; i < len; i++) { dp[i % 2][0] = max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i]); dp[i % 2][1] = max(dp[(i - 1) % 2][1], prices[i] + dp[(i - 1) % 2][0]); } return dp[(len - 1) % 2][1]; } };
123. 买卖股票的最佳时机 III
题目链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
文章讲解:https://programmercarl.com/0123.买卖股票的最佳时机III.html
题目难度:困难
视频讲解:https://www.bilibili.com/video/BV1WG411K7AR
题目状态:没思路
思路:
这次的动规数组主要维护的是五个状态:
- 无操作
- 第一次持有股票
- 第一次不持有股票
- 第二次持有股票
- 第二次不持有股票
dp[i][j]
中i表示第i天,j就是上面五个状态,dp[i][j]
表示第i天状态j的所剩最大现金。
因此:每个状态就会有两种操作:
- 有操作:假设是状态1,那么就表示当天买入股票了,则
dp[i][1] = dp[i - 1][0] - prices[i]
。 - 没操作:就是这天没进行买入操作,那么
dp[i][1] = dp[i - 1][1]
。
其他状态同上。
代码:
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); if(len == 0) return 0; vector<vector<int>> dp(len, vector<int>(5)); dp[0][1] = -prices[0]; dp[0][3] = -prices[0]; for(int i = 1; i < len; ++i) { dp[i][0] = dp[i - 1][0]; dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i]); dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + prices[i]); 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]); } return dp[len - 1][4]; } };
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?