121. 买卖股票的最佳时机
贪心法
取左边最小的,记录出现过的最大差值
cpp
int maxProfit(vector<int>& prices) {
int low = INT_MAX;
int result = INT_MIN;
for(int price: prices){
low = min(low, price);
result = max(result, price - low);
}
return result;
}
动态规划
状态 0 :第 i 天结束后,不持有股票的最大利润。
状态 1 :第 i 天结束后,持有股票的最大利润。
dp[i][0] 表示第 i 天不持有股票的最大利润,dp[i][1] 表示第 i 天持有股票的最大利润。
状态 0(不持有股票)的两种可能:
情况 1:第 i-1 天就不持有股票,第 i 天啥也没做,利润不变 → dp[i][0] = dp[i-1][0]。
情况 2:第 i-1 天持有股票,第 i 天卖出了,利润 = 前一天持有股票的利润 + 当天股价 → dp[i][0] = dp[i-1][1] + prices[i]。
综上,dp[i][0] = max(dp[i-1][0], dp[i-1][1] + prices[i])(取两种情况的最大值)。
状态 1(持有股票)的两种可能:
情况 1:第 i-1 天就持有股票,第 i 天啥也没做,利润不变 → dp[i][1] = dp[i-1][1]。
情况 2:第 i 天是第一次买入股票,利润 = - 当天股价(因为买入要花钱,利润为负) → dp[i][1] = -prices[i]。
综上,dp[i][1] = max(dp[i-1][1], -prices[i])(取两种情况的最大值,本质是找「最便宜的买入价」)。
初始化状态
第 0 天(第一天):
不持有股票:没买也没卖,利润为 0 → dp[0][0] = 0。
持有股票:第一天买入,利润为 -prices[0] → dp[0][1] = -prices[0]。
cpp
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2, 0));
if(prices.size() == 0){
return 0;
}
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];
}
122. 买卖股票的最佳时机 II
贪心法
cpp
int maxProfit(vector<int>& prices) {
int res = 0;
for(int i = 1; i < prices.size(); i++){
if(prices[i] - prices[i - 1] > 0){
res += prices[i] - prices[i - 1];
}
}
return res;
}
动态规划
与上题类似,只是允许多次买卖
所以在买入股票需要叠加之前获得的最大利润
dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - prices[i])
cpp
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector<int>(2));
if(prices.size() == 0){
return 0;
}
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];
}
123. 买卖股票的最佳时机 III
有五种状态:
没有操作
第一次持有股票
第一次不持有股票
第二次持有股票
第二次不持有股票
cpp
int maxProfit(vector<int>& prices) {
vector<vector<int>>dp(prices.size(), vector<int>(5));
if(prices.size() == 0){
return 0;
}
dp[0][0] = 0;
dp[0][1] = -prices[0];
dp[0][2] = 0;
dp[0][3] = -prices[0];
dp[0][4] = 0;
for(int i = 1; i < prices.size(); i++){
dp[i][0] = dp[i - 1][0];//不操作一直是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];
}
188. 买卖股票的最佳时机 IV
是上题的扩展,有k次买 k次卖,就有2 * k + 1个状态,第一个状态为不操作,其余是第i次持有,以及第i次不持有
cpp
int maxProfit(int k, vector<int>& prices) {
vector<vector<int>>dp(prices.size(), vector<int>(2*k + 1));
if(prices.size() == 0){
return 0;
}
for(int i = 1; i < 2*k + 1; i++){
if(i % 2 == 1){
dp[0][i] = -prices[0];
}else {
dp[0][i] = 0;
}
}
for(int i = 1; i < prices.size(); i++){
dp[i][0] = 0;
for(int j = 1; j < 2*k + 1; 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][2 * k];
}
309. 买卖股票的最佳时机含冷冻期
分成四个状态:
今天持有股票:
继承昨日已买的股票
今日买股票
今天卖出股票
今天继续不买股票(非冷冻期):
昨日冷冻期
昨日非冷冻期
今天冷冻期
cpp
class Solution {
public:
int maxProfit(vector<int>& prices) {
if(prices.size() <= 1){
return 0;
}
vector<vector<int>> dp(prices.size(), vector<int>(4, 0));
//四种状态:今天持有股票,今天卖出,今天保持不买(非冷冻期),今天冷冻期
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 0;
for(int i = 1; i < prices.size(); i++){
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][2] - prices[i], dp[i - 1][3] - prices[i]));
dp[i][1] = dp[i - 1][0] + prices[i];
dp[i][2] = max(dp[i - 1][2], dp[i - 1][3]);
dp[i][3] = dp[i - 1][1];
}
return max(dp[prices.size() - 1][1],max(dp[prices.size() - 1][2], dp[prices.size() - 1][3]));
}
};
714. 买卖股票的最佳时机含手续费
与第二题类似,只是卖出的时候多扣一个手续费
cpp
int maxProfit(vector<int>& prices, int fee) {
if(prices.size() <= 1){
return 0;
}
vector<vector<int>>dp(prices.size(), vector<int>(2, 0));
dp[0][0] = -prices[0];
dp[0][1] = 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] - fee);
}
return dp[prices.size() - 1][1];
}