123. 买卖股票的最佳时机 III - 力扣(Leetcode)
思路:
-
使用动态规划
-
-
确定dp数组以及下标的含义
dp[i][j]
的定义为:dp[i][0]
没有意义,仅用于占位,dp[i][1]
代表第一次持有股票的现金最大值,dp[i][2]
代表第一次不持有股票的现金最大值,dp[i][3]
代表第二次持有股票的现金最大值,dp[i][4]
代表第二次不持有股票的现金最大值
-
确定递推公式
// 状态转移方程 // dp[i][1]有两种状态,一种是本身就持有,那么值从dp[i - 1][1]演变而来 // 另一种是本身不持有,要购买一支股票,那么值就是-prices[i],取两者最大值 dp[i % 2][1] = max(dp[(i - 1) % 2][1], -prices[i]); // dp[i][2]有两种状态,一种是本身就不持有,那么值从dp[i - 1][2]演变而来 // 另一种是今天卖了手上的股票,那么值就是dp[i - 1][1] + prices[i],取两者最大值 dp[i % 2][2] = max(dp[(i - 1) % 2][2], dp[(i - 1) % 2][1] + prices[i]); // dp[i][3]有两种状态,一种是本身就持有,那么值从dp[i - 1][3]演变而来 // 另一种是本身不持有,要购买一支股票,但是此时的值要继承第一次不持有股票的值 // 所以此时的值就是dp[i - 1][2] - prices[i] dp[i % 2][3] = max(dp[(i - 1) % 2][3], dp[(i - 1) % 2][2] - prices[i]); // dp[i][4]有两种状态,一种是本身就不持有,那么值从dp[i - 1][4]演变而来 // 另一种是今天卖了手上的股票,那么值就是dp[i - 1][3] + prices[i],取两者最大值 dp[i % 2][4] = max(dp[(i - 1) % 2][4], dp[(i - 1) % 2][3] + prices[i]);
-
dp数组如何初始化
vector< vector<int> > dp(2, vector<int>(5, 0)); dp[0][1] = -prices[0]; dp[0][3] = -prices[0];
-
确定遍历顺序
因为i = 0已经初始化,所以从i = 1开始从前往后遍历
-
我的AC代码
// 时间复杂度O(n),空间复杂度O(1)
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector< vector<int> > dp(2, vector<int>(5, 0));
int len = prices.size();
dp[0][1] = -prices[0];
dp[0][3] = -prices[0];
for(int i = 1; i < len; ++i) {
dp[i % 2][1] = max(dp[(i - 1) % 2][1], -prices[i]);
dp[i % 2][2] = max(dp[(i - 1) % 2][2], dp[(i - 1) % 2][1] + prices[i]);
dp[i % 2][3] = max(dp[(i - 1) % 2][3], dp[(i - 1) % 2][2] - prices[i]);
dp[i % 2][4] = max(dp[(i - 1) % 2][4], dp[(i - 1) % 2][3] + prices[i]);
}
return dp[(len - 1) % 2][4];
}
};
标准答案
空间复杂度高但便于理解
// 时间复杂度O(n),空间复杂度O(n)
// 这个版本空间复杂度高,但是便于理解
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];
}
};
空间复杂度低但不易于理解
// 时间复杂度O(n),空间复杂度O(1)
// 空间复杂度低但不易于理解
class Solution {
public:
int maxProfit(vector<int>& prices) {
if (prices.size() == 0) return 0;
vector<int> dp(5, 0);
dp[1] = -prices[0];
dp[3] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[1] = max(dp[1], dp[0] - prices[i]);
dp[2] = max(dp[2], dp[1] + prices[i]);
dp[3] = max(dp[3], dp[2] - prices[i]);
dp[4] = max(dp[4], dp[3] + prices[i]);
}
return dp[4];
}
};
188. 买卖股票的最佳时机 IV - 力扣(Leetcode)
思路:
-
使用动态规划,这道题和123. 买卖股票的最佳时机 III - 力扣(Leetcode)的最大区别就是最大交易次数不同,本题最大交易次数是k次,而123. 买卖股票的最佳时机 III - 力扣(Leetcode)最大交易次数是固定的2次,写上一题的时候我就在想有没有可能会出一道题让交易次数可变,没想到还真有,理清逻辑,掌握交易背后的本质是写出这道题的关键
-
我们用时间复杂度较高但较好理解的解法来做解析
-
-
确定dp数组以及下标的含义
dp[i][j]
的定义为:dp[i][0]
没有意义,仅用于占位,dp[i][k * 2 + 1]
代表第k + 1
次持有股票的现金最大值,dp[i][k * 2 + 2]
代表第k + 1
次不持有股票的现金最大值
-
确定递推公式
// 状态转移方程 for(int j = 1; j <= k * 2; ++j) { // dp[i][j](j为单数)有两种状态,一种是本身就持有,那么值从dp[i - 1][j]演变而来 // 另一种是本身不持有,要购买一支股票,其值要从上一次卖出股票那里继承 // 如果是第一次买股票就是从dp[i][0]那里继承 // 那么值就是dp[i - 1][j - 1] - prices[i],取两者最大值 if(j % 2 == 1) { dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]); } // dp[i][j](j为双数)有两种状态,一种是本身就不持有,那么值从dp[i - 1][j]演变而来 // 另一种是卖了手上的股票,其值要从上一次持有股票那里继承 // 那么值就是dp[i - 1][j - 1] + prices[i],取两者最大值 else { dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]); } }
-
dp数组如何初始化
vector< vector<int> > dp(prices.size(), vector<int>(k * 2 + 1, 0)); for(int i = 1; i <= k * 2; ++i) { if(i % 2 == 1) { dp[0][i] = -prices[0]; } }
-
确定遍历顺序
因为i = 0已经初始化,所以从i = 1开始从前往后遍历
-
我的AC代码
空间复杂度高
// 时间复杂度O(n * k),空间复杂度O(n * k)
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
vector< vector<int> > dp(prices.size(), vector<int>(k * 2 + 1, 0));
for(int i = 1; i <= k * 2; ++i) {
if(i % 2 == 1) {
dp[0][i] = -prices[0];
}
}
for(int i = 1; i < prices.size(); ++i) {
for(int j = 1; j <= k * 2; ++j) {
if(j % 2 == 1) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] - prices[i]);
}
else {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i]);
}
}
}
return dp[prices.size() - 1][k * 2];
}
};
优化空间复杂度
// 时间复杂度O(n * k),空间复杂度O(k)
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
vector<int> dp(k * 2 + 1, 0);
for(int i = 1; i <= k * 2; ++i) {
if(i % 2 == 1) {
dp[i] = -prices[0];
}
}
for(int i = 1; i < prices.size(); ++i) {
for(int j = 1; j <= k * 2; ++j) {
if(j % 2 == 1) {
dp[j] = max(dp[j], dp[j - 1] - prices[i]);
}
else {
dp[j] = max(dp[j], dp[j - 1] + prices[i]);
}
}
}
return dp[k * 2];
}
};
标准答案
// 时间复杂度O(n * k),空间复杂度O(n * k)
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];
}
};
Comments NOTHING