买卖股票的最佳时机
题目表述
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
样例
Ex.1
输入:[7,1,5,3,6,4]
输出:5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
Ex.2
输入: prices = [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
题解
很简单,直接看代码吧。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if(n<2) return 0;
int buy = -prices[0], sell = 0;
for(int i=1;i<prices.size();i++){
buy = max(buy, -prices[i]); // buy 始终是截止到第 i 天股票最便宜价格的相反数
sell = max(sell, buy + prices[i]); // 卖出,这里可以使用更新过后的buy
}
return sell;
}
};
买卖股票的最佳时机 II
题目描述看链接
题解
我们定义两个数组 b u y [ i ] , s e l l [ i ] buy[i], sell[i] buy[i],sell[i], 其中, 0 < = i < n 0 <=i<n 0<=i<n。
b
u
y
[
i
]
buy[i]
buy[i],表示在第i天且持有一只股票的前提下的最大收益。
s
e
l
l
[
i
]
sell[i]
sell[i],表示在第i天且手中没有股票的前提下的最大收益。
那么状态转移方程为:
b
u
y
[
i
]
=
m
a
x
(
b
u
y
[
i
−
1
]
,
−
p
r
i
c
e
s
[
i
]
)
buy[i] = max(buy[i-1], -prices[i])
buy[i]=max(buy[i−1],−prices[i])
s
e
l
l
[
i
]
=
m
a
x
(
s
e
l
l
[
i
−
1
]
,
b
u
y
[
i
]
+
p
r
i
c
e
s
[
i
]
)
sell[i] = max(sell[i-1], buy[i]+prices[i])
sell[i]=max(sell[i−1],buy[i]+prices[i])
边界条件:
b
u
y
[
0
]
=
−
p
r
i
c
e
s
[
0
]
s
e
l
l
[
0
]
=
0
buy[0] = -prices[0] \\ sell[0] = 0
buy[0]=−prices[0]sell[0]=0
刚开始,持有一只股票的话就是负收益。
代码
我们只需要从前一天的结果推导出后一天的结果。因此不需要使用数组来存储数据。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if(n<2) return 0;
int sell = 0;
int buy = -prices[0];
for(int i=1;i<n;i++){
buy = max(buy, sell-prices[i]);
sell = max(sell, buy+prices[i]); // 因为我们不需要手续费,我们不需要保持buy是第i-1填的最优值。 假如在上一步buy被更新了,而在更新sell的时候又会加回来,还是等于sell。不会影响sell的更新。
}
return sell;
}
};
买卖股票的最佳时机 III
题目看链接
题解
两种思路:
- 我们把数组分成两部分,一半求一个最优解。然后拼接一起。
- 我们从前往后得到最好的只交易一次的数组 s e l l [ 0 − n ] sell[0-n] sell[0−n]。
- 然后到这遍历数组得到最好的之交易一次的数组 l l e s [ 0 − n ] lles[0-n] lles[0−n]。
- 最后我们遍历分割线找 max { s e l l [ i ] + l l e s [ i ] } \max\{sell[i]+lles[i]\} max{sell[i]+lles[i]}。
- 我们定义四种状态。卖了一只股的收益,卖了两只股的收益,手中持有一只股的收益,手中持有两只股的收益。
上面四种状态我们分别定义为 s e l l 0 , s e l l 1 , b u y 0 , b u y 1 sell_0, sell_1, buy_0, buy_1 sell0,sell1,buy0,buy1。我们始终保持上面四种状态的最大收益。
在第i天的状态。
- 第一次买卖:
- b u y 0 = m a x ( b u y 0 , − p r i c e s [ i ] ) buy_0 = max(buy_0, -prices[i]) buy0=max(buy0,−prices[i]) # 始终为负数,因为还没买股票回本。始终保存最低的股价。
- s e l l 0 = m a x ( s e l l 0 , b u y 0 + p r i c e s [ i ] ) sell_0 = max(sell_0, buy_0+prices[i]) sell0=max(sell0,buy0+prices[i]) # 将该股票卖了之后的收益。该值也是一次买卖的最优值。是问题1的结果。
- 第二次买卖:
- b u y 1 = m a x ( b u y 1 , s e l l 0 − p r i c e s [ i ] ) buy_1 = max(buy_1, sell_0-prices[i]) buy1=max(buy1,sell0−prices[i]) # 第二次买股票是在第一次买卖之后的最大收益的基础上进行的。且始终保持买了第二只股票后收益最大。
- s e l l 1 = m a x ( s e l l 1 , b u y 1 + p r i c e s [ i ] ) sell_1 = max(sell_1, buy_1+prices[i]) sell1=max(sell1,buy1+prices[i]) # 将第二只股票买了之后的最大收益。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if(n<2) return 0;
int sell[2], buy[2];
sell[0] = 0, sell[1] = 0;
buy[0] = -prices[0], buy[1] = -prices[0];
for(int i=1;i<n;i++){
buy[0] = max(buy[0], -prices[i]); // 买第一只
sell[0] = max(sell[0], buy[0]+prices[i]); // 继续保持现在的金额,或是卖出仅有的股票
// if(i>=3)
buy[1] = max(buy[1], sell[0]-prices[i]); // 买第二只
// if(i>=3)
sell[1] = max(sell[1], buy[1]+prices[i]); // 买二只中的一只
}
return sell[1];
}
};
买卖股票的最佳时机 IV
题目看链接
题解
按照第三题的方法2,我们只需保存一个2*k个买卖的状态就行。每次按顺序更新一下即可。
代码
class Solution {
public:
int maxProfit(int k, vector<int>& prices) {
int n = prices.size();
vector<int> buy(k+1, -prices[0]);
vector<int> sell(k+1, 0);
for(int i=0;i<n;i++){
for(int j=0;j<k;j++){
if(j==0){
buy[0] = max(buy[0], -prices[i]);
sell[0] = max(sell[0], buy[0]+prices[i]);
}
else{
buy[j] = max(buy[j], sell[j-1]-prices[i]);
sell[j] = max(sell[j], buy[j]+prices[i]);
}
}
}
return sell[k-1];
}
};
买卖股票的最佳时机 含冷冻期
题目看链接
题解
在含有冷冻期的情况下,我们就不能使用前一天的交易完的最优值,而是使用前两天交易完的的最优值去更新下一次买股票的最大收益。这就需要我们最初历史sell的值。
代码
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if(n<2) return 0;
int buy = -prices[0];
vector<int> sell(n+1, 0);
for(int i=1;i<n;i++){
if(i-2<0)
buy = max(buy, -prices[i]);
else
buy = max(buy, sell[i-2]-prices[i]); // 含有冷工期,想要买这i填的股票。你需要在i-2天的完结交易状态的基础上进行。
sell[i] = max(sell[i-1], buy+prices[i]);
}
return sell[n-1];
}
};
买卖股票的最佳时机 含手续费
题目看链接
题解
因为含有手续费,我们就不能把第二问的代码照搬过来。我们在根据前一天的最佳buy更新今天的最优buy时,需要把前一天的buy保存下来。
因为不含有手续费时,我们使用今天的buy进行更新sell会包含当天买卖的情况。但是保函手续费是,这种情况就包含不了了。
代码
class Solution {
public:
int maxProfit(vector<int>& prices, int fee) {
int n = prices.size();
if(n<2) return 0;
int buy = -prices[0], sell = 0;
for(int i=1;i<n;i++){
int prebuy = buy; // 前一天买股票的最大收益
buy = max(buy, sell-prices[i]); // 这一天买股票的最大收益
sell = max(sell, prebuy+prices[i]-fee); // 今天 卖出 前一天买过的股票 的最大收益
}
return sell;
}
};