动态规划专练:力扣第121、122题

力扣第121题-买卖股票的最佳时机

2026.8.6

1.因为只买卖一次,所以很容易想到使用遍历一遍数组的贪心算法,遍历过程中更新遇到的最小值,并不断更新利润最大值。完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // low 记录遍历到当前为止的历史最低股价
 3.     int low = INT_MAX;
 4.     // res 记录最大利润,初始为0(不交易利润为0)
 5.     int res = 0;
 6.     for (int i = 0; i < pricesSize; i++){
 7.         // 更新最低买入价格
 8.         low = fmin(low, prices[i]);
 9.         // 当天卖出,更新最大利润
10.         res = fmax(res, prices[i] - low);
11.     }
12.     return res;
13. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

2026.8.7

1.本题也可以使用动态规划来解决。当天的状态只有两种:持有股票和未持有股票,所以可以设置一个二维数组dppricesSize2,dpi0代表第i天持有股票的最大现金,dpi1代表第i未持有股票的最大现金。初始现金为0,买入股票后现金变为-pricesi

2.分类讨论来推导出递推公式:

(1)持有股票。又分为两种情况:之前买入和今天买入,对应的金额分别为dpi - 10和-pricesi,则递推公式为dpi0 = fmax(dpi - 10, -pricesi)。

(2)未持有股票。又分为两种情况:之前就没有和今天卖出,对应金额分别为dpi - 11和dpi - 10 + pricesi,则递推公式为dpi1 = fmax(dpi - 11, dpi - 10 + pricesi)。

3.由于最后卖出股票(即未持有股票)的收益一定大于持有股票,所以最后返回结果一定为dppricesSize -- 11

4.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // dp[i][0]:第i天持有股票的最大利润
 3.     // dp[i][1]:第i天不持有股票的最大利润
 4.     int dp[pricesSize][2];
 5.  
 6.     // 第0天持有:只能当天买入,利润为 -股价
 7.     dp[0][0] = -prices[0];
 8.     // 第0天不持有:无操作,利润0
 9.     dp[0][1] = 0;
10.  
11.     for (int i = 1; i < pricesSize; i++){
12.         // 今天持有两种可能:之前就持有 / 今天刚买入
13.         dp[i][0] = fmax(dp[i - 1][0], -prices[i]);
14.         // 今天不持有两种可能:之前就不持有 / 今天卖出昨天持有的
15.         dp[i][1] = fmax(dp[i - 1][1], dp[i - 1][0] + prices[i]);
16.     }
17.  
18.     // 最后一天一定不持有股票利润最大(持有一定亏钱)
19.     return dp[pricesSize - 1][1];
20. }

该算法时间复杂度和空间复杂度均为O(n)。

5.发现递推公式实际上只关心前一项,所以将dp数组的大小减小为dp22来节省空间。完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 滚动dp数组,dp[0]代表前一天状态,dp[1]代表当天状态
 3.     // dp[x][0]:持有股票最大利润
 4.     // dp[x][1]:不持有股票最大利润
 5.     int dp[2][2];
 6.  
 7.     // 初始化第0天(第一天)
 8.     dp[0][0] = -prices[0]; // 当天买入,持有股票
 9.     dp[0][1] = 0;          // 不操作,无股票
10.  
11.     // 从第二天开始遍历
12.     for (int i = 1; i < pricesSize; i++){
13.         // 当天持有:昨天就持有 | 今天第一次买入
14.         dp[1][0] = fmax(dp[0][0], -prices[i]);
15.         // 当天不持有:昨天就无股票 | 昨天持有今天卖出
16.         dp[1][1] = fmax(dp[0][1], dp[0][0] + prices[i]);
17.         // 更新前一天状态为今天,供下一轮循环使用
18.         dp[0][0] = dp[1][0];
19.         dp[0][1] = dp[1][1];
20.     }
21.  
22.     // 最后一天不持有股票为答案
23.     return dp[0][1];
24. }

该算法时间复杂度为O(n),空间复杂度为O(1)。因为每次for循环都会将dp1的值赋给dp0,最后返回的应选择dp01,以防数组长度只有1而导致dp11是垃圾值。

力扣第122题-买卖股票的最佳时机Ⅱ

2026.7.31

1.为了利益最大化,将股票的每日价格看成一个折线图,在低点买入高位卖出就一定能赚钱,实际上就是寻找数组的极值点。使用贪心算法,完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 只有一天股价,无法完成买卖,利润为0
 3.     if (pricesSize == 1) return 0;
 4.  
 5.     // res 累计总利润
 6.     int res = 0;
 7.     // buy 记录买入价格,-1代表当前无持仓
 8.     int buy = -1;
 9.     // 从第二天股价开始遍历
10.     for (int i = 1; i < pricesSize; i++){
11.         // 当前无持仓,且当天股价高于前一天,在前一天买入
12.         if (buy == -1 && prices[i] > prices[i - 1]){
13.             buy = prices[i - 1];
14.         }
15.         // 当前持有股票
16.         if (buy >= 0){
17.             // 当天股价低于前一天,前一天卖出,结算利润,清空持仓
18.             if (prices[i] < prices[i - 1]){
19.                 res += prices[i - 1] - buy;
20.                 buy = -1;
21.             } else if (i == pricesSize - 1){
22.                 // 遍历到最后一天仍持有股票,当天卖出结算利润
23.                 res += prices[i] - buy;
24.                 buy = -1;
25.             }
26.         }
27.     }
28.  
29.     return res;
30. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

2.官方的贪心算法代码写的极其简单,如果第二天比第一天的股票价格贵就买入并卖出。因为题目上强调了可以当天同时买入与卖出,行为上复杂化且等价于寻找波峰波谷,但代码就会变得非常简单:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // res 存放累计的最大利润
 3.     int res = 0;
 4.     // 只有一天价格,无法进行买卖,利润为0
 5.     if (pricesSize == 1) return 0;
 6.     // 从第二天开始遍历所有价格
 7.     for (int i = 1; i < pricesSize; i++){
 8.         // 相邻两天差价为正就累加利润,差价为0或负数不加
 9.         res += fmax(0, prices[i] - prices[i - 1]);
10.     }
11.  
12.     return res;
13. }

3.官方答案还有动态规划算法,看起来比贪心算法要复杂,当下就不再尝试了。

2026.8.7

1.本题相比于力扣第121题-买卖股票的最佳时机,唯一的区别就在于本题能无限次买卖,所以递推公式上有一定区别,主要体现在"当天持有股票"部分,递推公式从dp10 = fmax(dp00, -pricesi)变为了dp10 = fmax(dp00, dp01 - pricesi)。121题只允许买卖一次,如果当天购买股票就说明之前的现金一定是0,所以最后的金额为-pricesi;122题允许买卖多次,之前未持有股票时的现金就应该是dp01 - pricesi

2.基于以上思想,可写出完整代码如下:

cpp 复制代码
 1. int maxProfit(int* prices, int pricesSize) {
 2.     // 滚动DP数组,dp[0]代表前一天状态,dp[1]代表当日状态
 3.     // dp[x][0]:持有股票最大利润;dp[x][1]:不持有股票最大利润
 4.     int dp[2][2];
 5.     // 第一天:持股则买入股票,利润-股价;不持股无操作利润0
 6.     dp[0][0] = -prices[0];
 7.     dp[0][1] = 0;
 8.     
 9.     for (int i = 1; i < pricesSize; i++){
10.         // 今日持股:之前就持有 / 昨天空仓今天买入(允许多次买卖)
11.         dp[1][0] = fmax(dp[0][0], dp[0][1] - prices[i]);
12.         // 今日空仓:之前就空仓 / 昨天持股今天卖出
13.         dp[1][1] = fmax(dp[0][1], dp[0][0] + prices[i]);
14.         // 更新前一天状态为当天结果,供下一轮循环使用
15.         dp[0][0] = dp[1][0];
16.         dp[0][1] = dp[1][1];
17.     }
18.     // 最后一天一定不持有股票收益最大,返回结果
19.     return dp[0][1];
20. }

该算法时间复杂度为O(n),空间复杂度为O(1)。

相关推荐
玖玥拾13 小时前
LeetCode 392 判断子序列
笔记·算法·leetcode
摇滚侠14 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 15
java·spring boot·笔记
摇滚侠14 小时前
《SpringBoot 3:入门与应用实战》第 7 章 AOP 思想与实现 阅读笔记 14
spring boot·笔记·后端
重生之后端学习14 小时前
239. 滑动窗口最大值[困难]✅
java·数据结构·算法·leetcode·职场和发展
fpcc15 小时前
算法和数据结构—动态规划法
数据结构·算法·动态规划
陈年老古董16 小时前
OpenCV图像处理笔记:图像拼接、答题卡识别与目标提取
人工智能·笔记·python·opencv·计算机视觉
陈年老古董17 小时前
CentOS编译安装Python3.11完整教程
linux·笔记·学习·centos·python3.11
星星.72217 小时前
C++算法竞赛|二分查找与二分答案:边界模板、浮点二分、STL
数据结构·c++·算法
sel_917 小时前
【OPD论文导读(二)】OPD(On-Policy Distillation)全景调研:十篇论文讲透“在自己生成的内容上学习“这件事
人工智能·python·深度学习·学习·算法·语言模型
动词ing17 小时前
【学习笔记】数据结构(数组长度和关键特性+快慢指针+左右指针)
数据结构·笔记·学习