121. 买卖股票的最佳时机
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
贪心
只能买卖一次
在某一天买入:希望在股票价格最低的一天买入;
在 未来的某一个不同的日子 卖出:在未来的价格最高的一天卖出;
如此,获得的利润最大;
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0;
int minPirce = prices[0];
for (int i = 1; i < prices.size(); i++) {
res = max(res, prices[i] - minPirce);
minPirce = min(minPirce, prices[i]);
}
return res;
}
};
动态规划
n
为表示股票价格数组的长度;
i
表示第 i
天(i
从 0
开始到 n - 1
);
k
表示允许(交易)的 最多 次数;
dp[i][k]
表示在第 i
天 最多 进行 k
笔交易获得的最大利润。
初始条件:dp[-1][k] = dp[i][0] = 0
,dp[-1][k]
这里第一天对应 i = 0
,所以第 -1
天没有股票交易;dp[i][0]
即如果没有交易,就不产生任何利润;
第 i
天可以:买入、卖出、不进行任何操作;目的是使最终利润最大;
本题只能交易一次;意味着全程最多持有一支股票;假设在第 i
天买入股票,则买入之前持有 0
支股票;假设在第 i
天卖出股票,则卖出之前正好持有 1
支股票;第 i
天持有几只股票会影响我们在第 i
天的操作,继而影响最大利润;
综上所述,对 dp[i][k] 的应该分为两个部分:dp[i][k][0]
和 dp[i][k][1]
:
dp[i][k][0]
表示第 i
天进行最多进行 k
笔交易并且完成以后,我们当前不持股而获得的利润;
dp[i][k][1]
表示第 i
天进行最多进行 k
笔交易并且完成以后,我们当前持股而获得的利润;
base cases:
dp[-1][k][0] = 0; // -1天
dp[-1][k][1] = -Infinity; // 没有进行股票交易时不允许持有股票
dp[i][0][0] = 0; // 没有交易,没有利润
dp[i][0][1] = -Infinity; // 没有进行股票交易时不允许持有股票
状态转移方程:
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
dp[i][k][0]
第 i
天只能是无任何操作或卖出;当第 i
天无任何操作时,当天持股为0,则第 i - 1
天所有交易完成后,为不持股状态dp[i - 1][k][0]
,即为无操作的最大利润;当第 i
天卖出时,卖出之前持股为1,则第 i - 1
天所有交易完成后,为持股状态dp[i - 1][k][1]
,卖出的最大利润为第 i - 1
天持股的最大利润加上第 i
天卖出股票的金额;
dp[i][k][1]
第 i
天只能是无任何操作或买入;当第 i
天无任何操作时,当天持股为1,则第 i - 1
天所有交易完成后,为持股状态dp[i - 1][k][1]
,即为无操作的最大利润;当第 i
天买入时,买入之前持股为0,则第 i - 1
天所有交易完成后,为持股状态dp[i - 1][k - 1][0]
(这里最大交易次数减少一次,是因为买入操作会使用一次交易(买入-卖出为一次交易)),买入的最大利润为第 i - 1
天持股的最大利润减去第 i
天买入股票的金额;
最终的最大利润是 dp[n - 1][k][0],因为结束时持有 0 份股票的收益一定大于持有 1 份股票的收益。
回到本题:k = 1
状态转移方程:
dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], - prices[i]);
因为dp[i][0][0] = 0;
优化为二维数组:
dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = max(dp[i - 1][1], - prices[i]);
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2));
dp[0][0] = 0;
dp[0][1] = -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], -prices[i]);
}
return dp[prices.size() - 1][0];
}
};
第 i
天的最大利润只和第 i - 1
天的最大利润相关,也可以继续优化为常数级
122.买卖股票的最佳时机II
给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润 。
可以买卖多次
贪心
class Solution {
public:
int maxProfit(vector<int>& prices) {
int res = 0;
for (int i = 0; i < prices.size() - 1; i++) {
int diff = prices[i + 1] - prices[i];
if (diff > 0) {
res += diff;
}
}
return res;
}
};
动态规划
本题:k = +Infinity
,同时最多可以持有一支股票;
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
因为k = +Infinity
,所以 k 和 k - 1 可以看成相同的;
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k][0] - prices[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]);
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2));
dp[0][0] = 0;
dp[0][1] = -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]);
}
return dp[prices.size() - 1][0];
}
};
第 i
天的最大利润只和第 i - 1
天的最大利润相关,也可以继续优化为常数级
123.买卖股票的最佳时机III
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
本题:最多买卖两次,k = 2
dp[i][k][0] = max(dp[i - 1][k][0], dp[i - 1][k][1] + prices[i]);
dp[i][k][1] = max(dp[i - 1][k][1], dp[i - 1][k - 1][0] - prices[i]);
因为k = 2
,所以有:
dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
dp[i][1][1] = max(dp[i - 1][1][1], dp[i - 1][0][0] - prices[i]) = max(dp[i - 1][1][1], - prices[i]);
dp[i][2][0] = max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]);
dp[i][2][1] = max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]);
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<vector<int>>> dp(prices.size(), vector<vector<int>>(3, vector<int>(2)));
dp[0][1][0] = 0;
dp[0][1][1] = -prices[0];
dp[0][2][0] = 0;
dp[0][2][1] = -prices[0];
for (int i = 1; i < prices.size(); i++) {
dp[i][1][0] = max(dp[i - 1][1][0], dp[i - 1][1][1] + prices[i]);
dp[i][1][1] = max(dp[i - 1][1][1], - prices[i]);
dp[i][2][0] = max(dp[i - 1][2][0], dp[i - 1][2][1] + prices[i]);
dp[i][2][1] = max(dp[i - 1][2][1], dp[i - 1][1][0] - prices[i]);
}
return dp[prices.size() - 1][2][0];
}
};
第 i
天的最大利润只和第 i - 1
天的最大利润相关,也可以继续优化为常数级
class Solution {
public:
int maxProfit(vector<int>& prices) {
int profitOne0 = 0;
int profitOne1 = -prices[0];
int profitTwo0 = 0;
int profitTwo1 = -prices[0];
for (int i = 1; i < prices.size(); i++) {
profitOne0 = max(profitOne0, profitOne1 + prices[i]);
profitOne1 = max(profitOne1, - prices[i]);
profitTwo0 = max(profitTwo0, profitTwo1 + prices[i]);
profitTwo1 = max(profitTwo1, profitOne0 - prices[i]);
}
return profitTwo0;
}
};