题目1:121 买卖股票的最佳时机
题目链接:121 买卖股票的最佳时机
题意
价格数组prices中 第i个元素prices[i]表示一支股票第i天的价格
只能选择某一天买入这支股票,在未来的日子卖出该股票,计算能收获的最大利润,若不能获取任何利润,则返回0 注意股票只能买卖一次
动态规划
动规五部曲
1)dp数组及下标i的含义
dp[i][j] 表示第i天持有/不持有这支股票 只有2个状态 dp[i][0] dp[i][1]
dp[i][0] 表示持有股票获取的最大现金 dp[i][1] 表示不持有这支股票获取的最大现金
注意是持有,不是买入,持有是一个状态(包括买入)
最终求解dp[princes.size()-1][0] dp[prices.size()-1][1]的最大值
2)dp数组初始化
根据递推公式 依靠前一个状态推导后面的状态
dp[0][0] 第0天持有这支股票的最大现金数 dp[0][0] = -prices[0]
dp[0][1] 第0天不持有这支股票的最大现金数 dp[0][1] = 0
3)递推公式
在第i天持有股票收获的最大现金数可以由两种情况得到
1) 保持着之前持有股票的状态 2)在当天买入股票,因为是第1次买入股票,是0-prices[i]
dp[i][0] = max(dp[i-1][0],-prices[i])
在第i天不持有股票收获的最大现金数也同样由两种情况得到
1)保持着之前不持有股票的状态 2)在当天卖出股票
dp[i][1] = max(dp[i-1][1],dp[i-1][0]+prices[i])
4)遍历顺序
根据递推公式,后面状态依赖于前面的状态,从前向后遍历
5)打印dp数组
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
//定义dp数组
vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
//dp数组初始化
//第0天持有股票
dp[0][0] = -prices[0];
//第0天不持有股票
dp[0][1] = 0;
//遍历顺序
for(int i=1;i<prices.size();i++){
//第i天持有股票
dp[i][0] = max(dp[i-1][0], -prices[i]);
//第i天不持有股票
dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]);
}
return max(dp[prices.size()-1][0], dp[prices.size()-1][1]);
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
题目2:122 买卖股票最佳时机Ⅱ
题目链接:122 买卖股票最佳时机Ⅱ
题意
整数数组prices中的元素prices[i]表示某支股票第i天的价格,在每一天可以决定是否购买和出售股票,在任何时候只能持有1股股票,可以在当天买当天卖,返回获得的最大利润
注意:可以多次买入卖出
动态规划
动规五部曲
1)dp数组及下标i的含义
dp[i][j] 表示第i天持有/不持有股票的最大利润 有两个状态dp[i][0] dp[i][1]
dp[i][0] 表示第i天持有股票的最大利润
dp[i][1] 表示第i天不持有股票的最大利润
2)dp数组初始化
根据递推公式
dp[0][1] = 0
dp[0][0] = 0 - prices[0] = -prices[0]
3)递推公式
在第i天持有股票收获的最大现金数可以由两种情况得到
1) 保持着之前持有股票的状态 2)在当天买入股票 注意当前只能持有只有一支股票,所以再次购买前要出售掉之前的股票(这就涉及了不持有股票的状态),所以导致这次买入股票动作之前就可能有现金了 所以是dp[i-1][1]-prices[i]
第i天持有股票的最大利润 dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i])
在第i天不持有股票收获的最大现金数也同样由两种情况得到
1)保持着之前不持有股票的状态 2)在当天卖出股票
第i天不持有股票的最大利润 dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])
4)遍历顺序
根据递推公式,后面状态依赖于之前的状态,从前向后遍历
5)打印dp数组
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
//定义dp数组
vector<vector<int>> dp(prices.size(), vector<int>(2,0));
//dp数组初始化
//第0天持有股票
dp[0][0] = -prices[0];
//第0天不持有股票
dp[0][1] = 0;
//遍历顺序
for(int i=1;i<prices.size();i++){
//第i天持有股票
dp[i][0] = max(dp[i-1][0], dp[i-1][1]-prices[i]);
//第i天不持有股票
dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i]);
}
return max(dp[prices.size()-1][0], dp[prices.size()-1][1]);
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
题目3:123 买卖股票最佳时机Ⅲ
题目链接:123 买卖股票最佳时机Ⅲ
题意
prices数组中的元素prices[i]是一支给定的股票在第i天的价格,最多可以交易两次,计算所能获取的最大利润,注意:在再次购买前出售要出售之前的股票
动态规划
动规五部曲
1)dp数组及下标i的含义
dp[i][1] 第i天第1次持有
dp[i][2] 第i天第1次不持有
dp[i][3] 第i天第2次持有
dp[i][4] 第i天第2次不持有
2)dp数组初始化
dp[0][1] = -prices[0] 在第一天买
dp[0][2] = 0 在第一天卖,同一天买卖
dp[0][3] = -prices[0] 第一天又买入了
dp[0][4] = 0 第一天又卖出了
3)递推公式
1 第一次持有股票的状态
保持之前持有股票的状态 第一次买入股票
dp[i][1] = max(dp[i-1][1], -prices[i])
2 第一次不持有股票的状态
保持之前不持有股票的状态 第一次卖出股票(是在第一次持有股票的状态下)
dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i])
3 第二次持有股票的状态
保持之前持有股票的状态 第二次买入股票(是在第一次不持有股票的状态下)
dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i])
4 第二次不持有股票的状态
保持之前不持有股票的状态 第二次卖出股票(是在第二次持有股票的状态下)
dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i])
4)遍历顺序
根据递推公式 从前向后遍历
5)打印dp数组
卖出收获的现金肯定是多于买入的
max(dp[prices.size()-1][2], dp[prices.size()-1][4])
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
//定义dp数组
vector<vector<int>> dp(prices.size(), vector<int>(5, 0));
//初始化dp数组
//第一次持有股票
dp[0][1] = -prices[0];
//第一次不持有股票
dp[0][2] = 0;
//第二次持有股票 再一次在第一天买入股票
dp[0][3] = -prices[0];
//第二次不持有股票
dp[0][4] = 0;
//遍历顺序
for(int i=1;i<prices.size();i++){
//第1次持有股票
dp[i][1] = max(dp[i-1][1], -prices[i]);
//第1次不持有股票
dp[i][2] = max(dp[i-1][2], dp[i-1][1]+prices[i]);
//第2次持有股票
dp[i][3] = max(dp[i-1][3], dp[i-1][2]-prices[i]);
//第2次不持有股票
dp[i][4] = max(dp[i-1][4], dp[i-1][3]+prices[i]);
}
return max(dp[prices.size()-1][2], dp[prices.size()-1][4]);
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n × 5)
题目4:124 买卖股票最佳时机Ⅳ
题目链接:124 买卖股票的最佳时机Ⅳ
题意
整数数组prices中的元素pricces[i]表示给定的一支股票在第i天的价格 最多可以完成k笔交易 不能同时参与多笔交易
动态规划
动规五部曲
1)dp数组及下标i的含义
dp[i][j] 第i天某次持有/不持有股票的最大现金数
2)dp数组初始化
第i天奇数次持有股票的最大现金数(需要初始化) 第i天偶数次不持有股票最大现金数为0
for(int j=1;j<2*k;j+=2){
dp[0][j]= -prices[0]; //初始化奇数次持有股票的最大现金数
}
3)递推公式
第i天第1次持有状态 dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j]-prices[i])
第i天第1次不持有的状态 dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1]+prices[i])
4)遍历顺序
for(int i=1;i<prices.size();i++){
for(int j=0;j<2*k;j+=2){//j每次跳跃两个,才是下一次持有/不持有的状态
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]); //不持有股票
}
}
5)打印dp数组
代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
//定义dp数组
vector<vector<int>> dp(prices.size(), vector<int>(2*k+1, 0));
//初始化dp数组 第0天第1,2,3,...,k次持有股票的最大金币数 奇数次才持有股票
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;j+=2){
//第i天第1,2,3,...,k次持有股票
dp[i][j+1] = max(dp[i-1][j+1], dp[i-1][j]-prices[i]);
//第i天第1,2,3,...,k次不持有股票
dp[i][j+2] = max(dp[i-1][j+2], dp[i-1][j+1]+prices[i]);
}
}
return dp[prices.size()-1][2*k];//不持有股票
}
};
- 时间复杂度: O(n * k),其中 n 为 prices.size()
- 空间复杂度: O(n * k)