股票買賣系列總結
股票買賣系列的題目在面試中還是比較經典的,這里對這一系列做一些簡單的總結。
1. 只允許買賣一次
假設股票價格序列為(3, 5, 7, 3, 8, 1)
動態規划。整個過程中的行為選擇有3種,買/賣/無操作。
用\(dp[i]\)表示第\(i\)天的行為是”賣出“時,能得到的最大收益。顯然,我們固定了賣出的時間,只要在這個時間點之前的時間中選擇股票價格比最小的時候買入,就可以確定\(dp[i]\)的值。
所以我們可以遍歷價格序列,cur_min
不斷更新以記錄當前時間點之前的股票最低價格,所以\(dp[i] = max(0, prices[i]-cur\_min)\),最后所有dp[i]中的最大值就是只進行一次買賣能得到的最大利潤。可以在求\(dp[i]\)的同時用一個變量來記錄其最大值。
因為\(dp[i]\)只用到一次,所以沒必要開一個數組專門來存儲。
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 只允許最多一次交易 求最大收益
// 記錄當前最小 cur_min dp[i]表示在第i天賣出的最大收益
if(prices.size()<=1) return 0;
int cur_min = prices[0], res = 0;
for(int i=1; i<prices.size(); i++){
res = max(prices[i]-cur_min, res);// dp[i] = prices[i] - cur_min;
cur_min = min(cur_min, prices[i]);
}
return res;
}
};
2. 不限制買賣次數
假設股票價格序列為(1, 2, 3, 4, 5)
。其實這是一個特殊情況,我們可以在第0天買入,然后在最后一天賣出。此時最大收益就是4。
我們也可以在第0天買入,第一天賣出。第一天買入,第二天賣出。。。這樣的結果和上面是一樣的,最大收益都是4。
所以,對於比較常規的情況,比如說(7, 6, 3, 4, 5)
,在第一天和第二天是不能買入的,因為,之后找不到比當前價格更大的數了。另外的話,可以在3的時候買入,4的時候賣出,然后4又買入,5賣出。最后最大收益是2.
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 不限制股票的買賣次數
// 只要是遇到比當前價格更高的就賣掉
if(prices.size() <=1) return 0;
int buy = INT_MAX, profit = 0;
for(int i=0; i<prices.size(); i++){
if(prices[i]<=buy) buy = prices[i];
else{
profit += prices[i]-buy;
buy = prices[i];
}
}
return profit;
}
};
可以將上面代碼進行簡化:
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() <=1) return 0;
int buy = INT_MAX, profit = 0;
for(int i=0; i<prices.size(); i++){
if(prices[i] > buy) profit += prices[i] - buy;// 只有當當前價格大於假設買入的價格時,才進行賣出
buy = prices[i];// 每次都在當前進行“假設”買入
}
return profit;
}
};
3. 最多只能買賣2次
\(buy1\)表示在第\(i\)天進行第一次買入時可以獲得的最大收益
\(buy2\)表示在第\(i\)天進行第二次買入時可以獲得的最大收益
\(sell1\)表示在第\(i\)天進行第一次賣出時可以獲得的最大收益
\(sell2\)表示在第\(i\)天進行第二次賣出時可以獲得的最大收益
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 最多只能買賣2次 求最大收益
// buy1 sell1 buy2 sell2 分別表示當前天如果是第一次買/賣、第二次買/賣時的最大收益
int buy1 = INT_MIN, buy2 = INT_MIN, sell1 = 0, sell2 = 0;
for(int price:prices){
sell2 = max(sell2, buy2+price);
buy2 = max(buy2, sell1-price);
sell1 = max(sell1, buy1+price);
buy1 = max(buy1, -price);
}
return sell2;
}
};
4. 有冷凍期不限制買賣次數
\(buy[i]\)表示第\(i\)天之前最后一次行為是buy時,最大的收益
\(sell[i]\)表示第\(i\)天之前最后一次行為是sell時,最大的收益
\(rest[i]\)表示第\(i\)天之前最后一次行為是冷凍rest時,最大的收益
\(buy[i] = max(buy[i-1], rest[i-1] - prices[i])\),max(第i天冷凍, 第i天是賣出)
\(sell[i] = max(sell[i-1], buy[i-1] + prices[i])\),max(第i天冷凍,第i天是買入)
\(rest[i] = max(rest[i-1], buy[i-1], sell[i-1])\)
class Solution {
public:
int maxProfit(vector<int>& prices) {
// 有冷凍期 不限制買賣次數 但是賣完股票后有一天的冷凍期才能再接着買
int n = prices.size();
if(n<=1) return 0;
vector<int> buy(n, INT_MIN), sell(n, INT_MIN), rest(n, INT_MIN);
buy[0] = -prices[0];
sell[0] = 0;
rest[0] = 0;
for(int i=1; i<n; i++){
buy[i] = max(buy[i-1], rest[i-1]-prices[i]);
sell[i] = max(sell[i-1], buy[i-1]+prices[i]);
rest[i] = max(rest[i-1], max(buy[i-1], sell[i-1]));
}
return max(buy[n-1], max(sell[n-1], rest[n-1]));
}
};