买卖股票最佳时机
只买卖一次
只买卖一次的话,只需要在最低点买入,最高点卖出即可。
// 空间复杂度O(1)
// 从左到右
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (!n) return 0;
int ret = 0, minn = prices[0];
for (int i = 1; i < n; ++i) {
ret = max(ret, prices[i] - minn);
minn = min(minn, prices[i]);
}
return ret;
}
// 从右到左
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (!n) return 0;
int ret = 0, maxn = prices.back();
for (int i = n-1; i >= 0; --i) {
ret = max(ret, maxn - prices[i]);
maxn = max(maxn, prices[i]);
}
return ret;
}
限定买卖2次
根据只能买卖一次的做法,可以选择从左到右和从右到左两种方法,仅需预处理其中一种,然后再从另一边遍历即可。
// O(n),预处理左边,再从右边遍历
int maxProfit(vector<int>& prices) {
int n = prices.size();
if (!n) return 0;
vector<int> left(n);
int minn = prices[0];
for (int i = 1; i < n; ++i) {
left[i] = max(left[i-1], prices[i] - minn);
minn = min(minn, prices[i]);
}
int maxn = prices.back(), ret = 0;
// 如果必须要买卖2次,ret从0开始;
// 如果是最多买卖2次,ret从left.back()开始;
for (int i = n-1; i >= 1; --i) {
ret = max(ret, maxn - prices[i] + left[i-1]);
maxn = max(maxn, prices[i]);
}
return ret;
}
可买卖任意次
int maxProfit(vector<int>& prices) {
int n = prices.size();
int dp[n][2];
dp[0][0] = 0, dp[0][1] = -prices[0];
for (int i = 1; i < n; ++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[n-1][0];
}
int maxProfit(vector<int>& prices) {
int n = prices.size();
int dp0 = 0, dp1 = -prices[0];
for (int i = 1; i < n; ++i) {
int newDp0 = max(dp0, dp1 + prices[i]);
int newDp1 = max(dp1, dp0 - prices[i]);
dp0 = newDp0;
dp1 = newDp1;
}
return dp0;
}
int maxProfit(vector<int>& prices) {
int ans = 0, n = prices.size();
for (int i = 1; i < n; ++i)
ans += max(0, prices[i] - prices[i - 1]);
return ans;
}
限定最多买卖k次
动态规划:
buy[i][j]表示前i天交易j次,且当前持有股票的最大收益;
sell[i][j]表示前i天交易j次,且当前不持有股票的最大收益;
状态转移方程:
buy[i][j] = max(buy[i-1][j], sell[i-1][j]-prices[i]);
sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i]);
注意:
不合理的情况(如前i天交易了大于i/2的次数),设为-inf,将所有由不合理的状态推到出的新的状态也设为-inf;
int maxProfit(int k, vector<int>& prices) {
if (prices.empty()) return 0;
int n = prices.size(), kk = min(k, n/2);
vector<vector<int>> buy(n, vector<int>(kk+1)), sell(buy);
buy[0][0] = -prices[0];
for (int i = 1; i <= kk; ++i)
buy[0][i] = sell[0][i] = -1e9;
for (int i = 1; i < n; ++i) {
for (int j = 0; j <= kk; ++j) {
buy[i][j] = max(buy[i-1][j], sell[i-1][j]-prices[i]);
if (j) sell[i][j] = max(sell[i-1][j], buy[i-1][j-1]+prices[i]);
}
}
return *max_element(sell[n-1].begin(), sell[n-1].end());
}
空间优化:已知状态转移方程中,当前状态只与前一次交易的状态有关,因此只需要维护一组交易状态即可
int maxProfit(int k, vector<int>& prices) {
if (prices.empty()) return 0;
int n = prices.size(), kk = min(k, n/2);
vector<int> buy(kk+1), sell(buy);
buy[0] = -prices[0];
for (int i = 1; i <= kk; ++i)
buy[i] = sell[i] = -1e9;
for (int i = 1; i < n; ++i) {
for (int j = 0; j <= kk; ++j) {
buy[j] = max(buy[j], sell[j]-prices[i]);
if (j) sell[j] = max(sell[j], buy[j-1]+prices[i]);
}
}
return *max_element(sell.begin(), sell.end());
}