写题解仅为自我回顾
理论内容:
目录
1:买卖股票的最佳时机
思路解析:
本题有三种方法,逐一介绍
1:暴力解法
遍历两次数组,两个for循环,用每次遍历让result=max(result,prices[j]-prices[i])来记录并更新
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result=0;
for(int i=0;i<prices.size();i++)
{
for(int j=i+1;j<prices.size();j++)
{
result=max(result,prices[j]-prices[i]);
}
}
return result;
}
};
2:贪心算法
要使利润最大,那么就要保证我选择买的股票价格是最便宜的,我选择卖出的股票在那天是最贵的,并且买股票必须在卖股票之前;遍历一遍数组
用min取寻找最小的价格的股票:low=min(low,prices[i]);
用max取寻找最大的利润:result=max(result,prices[i]-low);
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result=0;
int low=INT_MAX;
for(int i=0;i<prices.size();i++)
{
low=min(low,prices[i]);
result=max(result,prices[i]-low);
}
return result;
}
};
3:动态规划
(1)设想:假设刚开始没有钱,手头的钱为0,那么dp根据列坐标可根据题意分为两类
···1:dp[i][0]持有股票时的所得的最多钱
持有股票相当于:我可能是今天买的,也可能是前几天买的,此时我持有股票
递推公式:dp[i][0]=max(dp[i-1][0],-prices[i])
解释:此时我持有股票,我啥都不干,我的钱没有损失,等于上一个的结果;
此时我想要买下这个股票,那么我的现金就要从0变成-prices[i]:当日股票的价格负数
···2:dp[i][1]不持有股票时所得的最多钱
不持有股票相当于:我可能是今天卖的,也可能是前几天卖的,此时我没有股票
递推公式:dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i])
解释:此时我不持有股票,我啥都不敢,我的钱没有损失,等于上一个的结果;
此时我想卖掉这个股票,那么我的现金就要从上一个的现金结果里+这个股票的售价
(2)创建二维dp数组:vector<vector<int>> dp(prices.size(),vector<int>(2))
因为要遍历这个prices数组,所以行的长度为prices.size()
因为要存储两种状态 [0] [1]:所以列的长度为:2
(3)初始化数组:
不难发现,这个dp数组的基石为:dp[0][0]和dp[0][1]
dp[0][0]含义:在第0天所持有股票的现金最大价值,既然是第0天,那么必然就是在一开始的时候就买下了股票,所以初始为:dp[0][0]=-prices[0];
dp[0][1]含义:在第0天所不持有股票现金的最大价值,意思就是我还没有股票,那么我一开始的钱又是0,所以初始化为:dp[0][1]=0
其他元素,为了不覆盖推导处理出来的元素初始化为0即可
(4)遍历顺序:
由递推公式我们可以知道,后一个是由前一个推导出来的,所以正序遍历即可
(5)返回结果
结合dp数组的含义:第i天所拥有的最多现金,那么肯定是卖了股票,才会使得现金增加,所以我们返回最后一天,不持有股票的dp:return dp[prices.size()-1][1]
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.empty()) return 0;
vector<vector<int> >dp(prices.size(),vector<int>(2,0));
dp[0][0]=-prices[0];
dp[0][1]=0;
for(int i=1;i<prices.size();i++)
{
dp[i][0]=max(dp[i-1][0],-prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return dp[prices.size()-1][1];
}
};
另外:
这样做的效率并不高,但主要是逻辑清晰,在下面的难题是你会有更深的体会,下面的题目只有特殊的才特别强调,模板都是一样的
2:买卖股票的最佳时机II
贪心解法:
只要后面的一个比我大,那我就把股票出售,计算此时的差价,并将差价累计起来
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int result=0;
for(int i=1;i<prices.size();i++)
{
if(prices[i]>prices[i-1])
{
result+=prices[i]-prices[i-1];
}
}
return result;
}
};
动态规划:
与上题不同的地方在于,本题可以进行多次交易,那么只需要一点地方改变即:
上一题的持有股票的递推公式为:dp[i][0]=max(dp[i-1][0],-prices[i]);
那么如果我们一开始的钱不是0呢?就比如我已经做过了一次交易,我兜里有些钱,那么你就不能像看穷光蛋一样,看我了,即:
用上一次不持有股票的最大现金-这个股票的价格(意味着,我买入这个股票)
所以本题的持有股票的递推公式为:dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i])
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.empty()) return 0;
vector<vector<int> >dp(prices.size(),vector<int>(2,0));
dp[0][0]=-prices[0];
dp[0][1]=0;
for(int i=1;i<prices.size();i++)
{
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
}
return dp[prices.size()-1][1];
}
};
3:买卖股票的最佳时机含手续费
思路解析:
动态规划:
相当于上一题加上了手续费而已,手续费是在卖出去的时候需要减去的东西
那么就很简单了:稍微改变第二个递推公式即可
dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]-fee)
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
if(prices.empty()) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
dp[0][0] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);
}
return dp[prices.size() - 1][1];
}
};
4:买卖股票的最佳时机III(困难)
思路解析:
动态规划:
与无限次买卖股票相比,乍一看最多只用搞两次,好像还比较简单,确实比较简单
只不过不能再像无限次一样只考虑两种状态了
总共有5种状态:
0:没有操作
1:第一次买,(第一次持有股票)->>初始化为-prices[0]
2:第一次卖,==上一次持有股票+股票的售价
3:第二次买,(第二次持有股票)->>初始化为-prices[0]
4:第二次卖,==上一次持有给股票+股票的手机
与上题的特殊不同在于,并不再是你推我我推你,而是:
0->1 1->2 2->3 3->4
其实没有0情况也是可以的,只是为了看起来逻辑清晰而已
关于第二次买入的初始化:
第二次买入依赖于第一次卖出的状态,其实相当于第0天第一次买入了,第一次卖出了,然后在买入一次(第二次买入),那么现在手头上没有现金,只要买入,现金就做相应的减少
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
dp[0][1] = -prices[0];
dp[0][3] = -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]);
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[prices.size() - 1][4];
}
};
5:买卖股票的最佳时机IV(困难)
思路解析:
动态规划:
还是一样的套路,但这次不是无限次,也不是2次,而是作为一个参数k次
但是通过上题我们已经发现了规律:总共有2*k+1个状态
0:啥都不干
1:第一次买入
2:第一次卖出
3:第二次买入
4:第二次卖出
5:第三次买入
6:第三次卖出
当次数为奇数次时:买入;当次数为偶数次时,卖出
我们总不能都像上题一样列出来吧,这样的是不可能完成的,重复性的工作:循环可以解决
在遍历的下一层加上:完美解决
for (int j = 0; j < 2 * k - 1; j += 2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
完整代码:
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<vector<int>> dp(prices.size(), vector<int>(2 * k + 1, 0));
for (int j = 1; j < 2 * k; j += 2) {
dp[0][j] = -prices[0];
}
for (int i = 1;i < prices.size(); i++) {
for (int j = 0; j < 2 * k - 1; j += 2) {
dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
}
}
return dp[prices.size() - 1][2 * k];
}
};
6:最佳买卖股票时机含冷冻期(最难)
思路解析:推导过程
分析状态共有多少种:
1:持有股票阶段(可能是之前买的,也可能是今天买入股票)
2:卖出股票阶段
····1:两天前就卖出了股票
····2:今天卖出了股票
4:处于冻结期内
推导状态方程
操作1:仍保持持有股票阶段:dp[i][0]=dp[i-1][0]
操作2:今天买入股票
····1:前一天是冷冻期:dp[i-1][3]-prices[i]
````2:前一天是保持卖出股票状态:dp[i-1][1]-prices[i]
操作二取最大值:max(dp[i - 1][3], dp[i - 1][1]) - prices[i]
那么dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i])
保持卖出股票阶段:
····1:前两天卖出股票,并且度过冰冻期
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
````2:今天卖出股票:
dp[i][2]=dp[i-1][0]+prices[i]
冰冻期:卖出股票的下一天进入冰冻期
dp[i][3]=dp[i-1][2]
初始化问题:
将持有股票的dp[0][0]=-prices[0]
最后结果问题:
一般是从状态二和状态三,卖完股票那天,或不持有股票股票的状态出发
但是,会有部分样例无法通过,因为有可能,是在冰冻期内产生最大值:卖完股票的下一天
代码实现:
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (n == 0) return 0;
vector<vector<int>> dp(n, vector<int>(4, 0));
dp[0][0] -= prices[0]; // 持股票
for (int i = 1; i < n; i++) {
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);
dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
dp[i][2] = dp[i - 1][0] + prices[i];
dp[i][3] = dp[i - 1][2];
}
return max(dp[n - 1][3],max(dp[n - 1][1], dp[n - 1][2]));
}
};
重申:题解仅为自我回顾,理论内容来自