LeetCode第3天 | 动态规划 | 20220715
动态规划
本文章参考了许多他人的笔记,仅供自己学习复习使用。
leetcode官网
Day3目录
不知道是不是因为是“股票”,感觉脑子不太转的来…
【第一题】 121.买卖股票的最佳时机
1.1 读题
- 已知:数组prices[0…i], 表示day0-day i 每天的股票价格
- 条件:i < j时,保证prices[i] <= prices[j]才可以卖出(这里带等号,因为可以同时满足“若无收益则输出0” 的条件)
- 输出:股票收益最大值
首先想到了遍历并用一个变量记录每轮的最大值,在时间复杂度O(n^2)可以求出答案,见1.2.1,但显然会超时。
再考虑如何一次遍历数组得出答案,见1.2.2.
1.2 解题
1.2.1 超时的暴力求解
class Solution {
public:
int maxProfit(vector<int>& prices) {
int ans=0;
int n = prices.size();
for(int i = 0; i< n;i++ ){
for(int j = i+1;j<n;j++){
if(prices[j] < prices[i]){
continue;
}
ans = max(ans,prices[j] - prices[i]);
}
}
return ans;
}
};
1.2.2 一次遍历
考虑第i天:如何使第i天卖出时股票收益最大——即,在第i天股票卖出,且在前面第0天到第i-1天中的股票价格最低时买入。所以当考虑第i天的收益时,只需要找到前i-1天中的最低价即可。
状态方程:maxprofit[i] = price[i] - minprice[i-1]
注意:其中minprice[i-1]
并不是指第i-1天的最低价,而是指前面i-1天中的最低价。
官网代码:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int inf = 1e9;
int minprice = inf, maxprofit = 0;
for (int price: prices) {
maxprofit = max(maxprofit, price - minprice);
minprice = min(price, minprice);
}
return maxprofit;
}
};
修改成好理解的:首先规定好maxprofit = 0
,且minprice = prices[0]
, 循环从i = 1开始:【注意,及时是i =1开始,maxprofit初始值也不应该是prices[0],因为第0天买第0天卖的收益仍然是0。】
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
int minprice = prices[0];
int maxprofit = 0;
for(int i =1;i<n;i++){
maxprofit = max(maxprofit, prices[i] - minprice);
minprice = min(minprice, prices[i]);
}
return maxprofit;
}
};
这题和昨天1014. 最佳观光组合的思路很像
【第二题】122. 买卖股票的最佳时机 II
2.1 读题
- 已知:数组prices[0…i], 表示day0-day i 每天的股票价格
- 条件:“你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。”
- 输出:能获得的最大利润
2.2 解题
状态转移:可以先购买,然后在 同一天 出售
原答主的代码为java,修改成cpp成功通过
class Solution {
public:
int maxProfit(vector<int>& prices) {
int len = prices.size();
if(len <2){ // len =0 or 1
return 0;
}
/*
cash: 持有现金
hold: 持有股票(若为负数表示持有多少股票,为正数说明前面转的比持股的还多了)
状态数组
状态转移:cash -> Hold -> cash -> hold -> cash ->hold -> cash; 每天可以转移,也可以不动,这儿不动的箭头没法画出来
*/
vector<int>cash(len);
vector<int>hold(len);// 买入股票后手中剩余的钱
// 一开始无法直接出售。虽然第0天可以出售,但是毫无意义:
// 买入prices[0]再卖出prices[0]利润为0
cash[0] = 0;
// 初始化买入prices[0],持股记对应股票价格的相反数
hold[0] = - prices[0];
for(int i = 1; i< len; i++){
// 以下两行对调也可以
cash[i] = max(cash[i-1], hold[i-1]+prices[i]);
hold[i] = max(hold[i-1], cash[i-1]-prices[i]);
/*
若hold[i-1] > cash[i-1]-prices[i],
*/
}
return cash[len-1];
}
};
手动模拟一边过程理解更清晰。
疑问:
a. 如何保证一时间最多只持有一股的?