分类一:Best Time to Buy and Sell Stock I/ II/ III/ IV/ with Cooldown

这是一个总结帖。总结一下相关题目。

分类一Best Time to Buy and Sell Stock I/ II/ III/ IV/ with Cooldown

121 Best Time to Buy and Sell Stock

Say you have an array for which the *i*th element is the price of a given stock on day i.
If you were only permitted to complete at most ONE transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1: Input: [7, 1, 5, 3, 6, 4] Output: 5
Example 2: Input: [7, 6, 4, 3, 1] Output: 0 (In this case, no transaction is done, i.e. max profit = 0.)

思路:一次DP. 同时维持minPrice 和 maxProfit这两个量跟第i天当前值的比较。

Code:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.empty()) return 0;
        int maxProfit = 0;
        int minPrice = INT_MAX;

        for(int i = 0; i < prices.size(); ++i){
            minPrice = min(minPrice, prices[i]);
            maxProfit = max(maxProfit, prices[i] - minPrice);
        }
        return maxProfit;
    }
};

122 Best Time to Buy and Sell Stock II

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as MANY transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

思路:既然是无限次交易,那么只要第i天比第i - 1天高,就在第i - 1天买入,第i天卖出。如果第i+1天比第i天更高,就再一次在第i天买入,第i + 1天卖出。所以直接把所有prices[i + 1] - prices[i] > 0的 prices[i + 1] - prices[i] 加在一起,就是最后profit。
Code:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    if(prices.empty()) return 0;

    int maxProfit = 0;
    for(int i = 0; i < prices.size() - 1; ++i){
        if(prices[i + 1] > prices[i])
            maxProfit += prices[i + 1] - prices[i];
    }
    return maxProfit;
    }
};

123 Best Time to Buy and Sell Stock III

Say you have an array for which the *i*th element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most TWO transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).

思路:两次DP,一次从前往后,记录截止到第i天之前已经完成一次交易的max profit,维持minPrice 和 maxProfit; 第二次从后往前,记录在第i天之后发生第二次交易的max profit,维持maxPrice,maxProfit2,res三个值。其中,maxProfit2 = max(maxPrice - prices[i], maxProfit2), res = max(res, maxProfit1[i] + maxProfit2)。
Code:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
    if(prices.empty()) return 0;

    vector<int> maxProfit1(prices.size(), 0);
    int minPrice = INT_MAX;
    int maxPrice = INT_MIN;
    int maxProfit2 = 0;
    int res = 0;

    for(int i = 0; i < prices.size(); ++i){
        minPrice = min(minPrice, prices[i]);
        maxProfit1[i] = max(maxProfit1[i - 1], prices[i] - minPrice);
    }

    for(int i = prices.size() - 1; i >= 0; --i){
        maxPrice = max(maxPrice, prices[i]);
        maxProfit2 = max(maxPrice - prices[i], maxProfit2);
        res = max(res, maxProfit1[i] + maxProfit2);
    }
    return res;
    }
};

188 Best Time to Buy and Sell Stock IV

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most k transactions.

Note:
You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
思路:如果k大于交易的天数,就变成了122 Best Time to Buy and Sell Stock II 无限交易次数的情况,这种情况我们写一个helper函数来应对。如果k不大于交易的天数,我们需要维护如下两个量:
global[i][j]:当前到达第i天最多可以进行j次交易,所得到的最大利润。
local[i][j]:当前到达第i天最多可以进行j次交易,而且最后一次交易在当天卖出,所得到的最大利润。
状态转移方程:
global[i][j] = max(local[i][j], global[i-1][j])
上述方程比较两个量的大小:①当前局部最大值;②过往全局最大值。
local[i][j] = max(global[i-1][j-1] + max(diff, 0), local[i-1][j] + diff)
上述方程比较两个量的大小:
①全局到i-1天进行j-1次交易,然后加上今天的交易(如果今天的交易赚钱的话)。
②取局部第i-1天进行j次交易,然后加上今天的差值(local[i-1][j]是第i-1天卖出的交易,它加上diff后变成第i天卖出,并不会增加交易次数。无论diff是正还是负都要加上,否则就不满足local[i][j]必须在最后一天卖出的条件了)

Code:

class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
    if(prices.empty()) return 0;
    if(k >= prices.size()) return helper(prices);

    vector<int> local(k + 1, 0);
    vector<int> global(k + 1, 0);
    int diff;

    for(int i = 0; i < prices.size() -1; ++i){
        diff = prices[i + 1] - prices[i];
        for(int j = k; j >= 1; --j){
            local[j] = max(global[j - 1] + max(diff, 0), local[j] + diff);
            global[j] = max(local[j], global[j]);
        }
    }
    return global[k];
    }

    int helper(vector<int>& prices){
        int profit = 0;

        for(int i = 1; i < prices.size(); ++i){
            if(prices[i] > prices[i - 1])
                profit = max(profit, profit + prices[i] - prices[i - 1]);
        }
        return profit;
    }
};

309 Best Time to Buy and Sell Stock with Cooldown

Say you have an array for which the ith element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete as MANY transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions:

You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day)
Example:

prices = [1, 2, 3, 0, 2]
maxProfit = 3
transactions = [buy, sell, cooldown, buy, sell]

思路:

维护三个一维数组buy, sell,和rest。根据他的解法,此题需要维护三个一维数组buy, sell,和rest。其中:
buy[i]表示在第i天的(最后一个)操作是买,此时的最大收益。
sell[i]表示在第i天的(最后一个)操作是卖,此时的最大收益。
rest[i]表示在第i天的(最后一个)操作是冷冻期,此时的最大收益。

我们写出递推式为:
buy[i] = max(rest[i-1] - price[i], buy[i-1])
sell[i] = max(buy[i-1] + price[i], sell[i-1])
rest[i] = max(sell[i-1], buy[i-1], rest[i-1])
解释如下:
buy[i]也就是在第i天的最后一个操作是买,那么最大收益就是两种情况中更大的:①冷冻期在第i - 1天结束,然后在第i天进行了买入操作,这时的最大收益是:冷冻期在第i - 1天结束时候的最大收益rest[i] — 在第i天执行了买入操作所以要花掉的钱 price[i],②在第i - 1天的时候已经进行了买入操作,然后在第i 天这天不做操作,此时的收益, 也就是buy[i-1]。

同理,sell[i]也就是在第i天的最后一个操作是卖,那么最大收益也是两种情况中更大的:①在第i天进行了卖出操作,这时的最大收益是:在第i - 1天这天进行了买入操作以后的最大收益 buy[i - 1] + 在第i天执行了卖出操作所以新到手的钱 price[i],②在第i - 1天的时候已经进行了卖出操作,然后在第i天这天不做操作,此时的收益, 也就是sell[i-1]。

最后,rest[i] = max(sell[i-1], buy[i-1], rest[i-1]) 就是最后一个操作是冷冻,这时候的最大收益是这三种情况中最大的:①在第i - 1天进行了卖出,然后在第i天做冷冻,此时的最大收益;②在第i - 1这天进行了买入,第i天这天不做操作,此时的最大收益;③在第i - 1这天已经开始了冷冻期,所以第i天这天不做操作,此时的最大收益。

上述递推式很好的表示了在买之前有冷冻期,买之前要卖掉之前的股票。
如何保证保证[buy, rest, buy]的情况不会出现呢?
这里有一个小技巧,这是由于buy[i] <= rest[i],为什么buy[i] <= rest[i]呢?
我们对比
buy[i] = max(rest[i-1]-price, buy[i-1]) 和 rest[i] = max(sell[i-1], buy[i-1], rest[i-1]) 其中的rest[i] = max(rest[i-1],buy[i-1]) 这一部分。会发现:
buy[i] = max(rest[i-1]-price, buy[i-1])
rest[i] = max(rest[i-1], buy[i-1])
其中buy[i] 的第一个比较项 rest[i-1] - price[i]一定小于rest[i]的第一项rest[i-1],因为price[i] > 0;
buy[i] 的 第二个比较项buy[i-1]和rest[i]的第二项相同;因此,buy[i] <= rest[i], 这样的话,rest[i] = max(sell[i-1], buy[i-1], rest[i-1]) 就可以简化成rest[i] = max(sell[i-1], rest[i-1]),这保证了[buy, rest, buy]不会出现。

进一步的,我们还发现rest[i] <= sell[i]也一定是满足的,所以rest[i] = max(sell[i-1], rest[i-1]) 又可以进一步简化成 rest[i] = sell[i-1],这样,我们可以将上面三个递推式精简到两个:

buy[i] = max(sell[i-2] - price, buy[i-1])
sell[i] = max(buy[i-1] + price, sell[i-1])

我们还可以做进一步优化,由于i只依赖于i-1和i-2,所以我们可以在O(1)的空间复杂度完成算法,参见代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buy = INT_MIN, pre_buy = 0, sell = 0, pre_sell = 0;
        for (int price : prices) {
            pre_buy = buy;
            buy = max(pre_sell - price, pre_buy);
            pre_sell = sell;
            sell = max(pre_buy + price, pre_sell);
        }
        return sell;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值