LeetCode 121/122/123/188/309 Best Time to Buy and Sell Stock Series

给出一个数组 代表股票价格
1 只允许交易一次
2 允许交易任意多次
3 最多交易两次
4 最多交易k次
5 允许交易任意多次 但是每次交易的时间有至少一天的cooldown期

1 我们只需要找到一前一后的ij指针 然后j指针减i指针差值最大即可。
从前往后遍历 维护一个截止到目前最小的price 维护另一个当前最大的profit/
核心代码:

for(int price: prices){
            min = Math.min(min, price);
            profit = Math.max(profit, price - min);
        }
        return profit;

2 这个实际上有点tricky,解法基于的原理是
我们把每一段上升的都加在一起 那么最终肯定大于等于 尾部-头部上升的。所以很简单 就是从头到尾遍历 只要后面大于前面 立刻把这个作为profit的一部分加到总profit里面。
核心代码:

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

第二种方法给予的原理:always choose the closest peak after a vally, 虽然听起来像greedy但是可以通过反证法证明。
其实这个和方法一基于的原理差不多,但是写法复杂了一些

class Solution {
    public int maxProfit(int[] prices) {
        if(prices == null || prices.length == 0) return 0;
        int i = 0;
        int valley = prices[0];
        int peak = prices[0];
        int maxprofit = 0;
        
        while(i < prices.length - 1) { //because we use prices[i+1] later, so i<prices.length-1
            while(i < prices.length - 1 && prices[i] >= prices[i+1]) { //why we use = in here? becasue we want to go as far as we can
                i++;
            }
            valley = prices[i]; //get a valley
            while(i < prices.length - 1 && prices[i] <= prices[i+1]) {
                i++;
            }
            peak = prices[i]; //get its closest peak
            maxprofit += peak - valley; //accumulative maxprofit
        }
        return maxprofit;
    }
}

3 这道题可以用DP去做 详见:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/solution/
但是我们可以用one pass 去做
核心代码如下:

for (int price : prices) {
        // the maximum profit if only one transaction is allowed
        t1Cost = Math.min(t1Cost, price);
        t1Profit = Math.max(t1Profit, price - t1Cost);
        // reinvest the gained profit in the second transaction
        t2Cost = Math.min(t2Cost, price - t1Profit);
        t2Profit = Math.max(t2Profit, price - t2Cost);
    }

4 最多K次 那这样的话 之前121-123的修改连就不能用了
这就不可避免的要DP了
dp[i][j] is defined as maximum profit from at most i transactions using prices[0…j]
so on day j, we have two options: do nothing, that makes dp[i][j] = dp[i][j-1]
or we choose to sell the stock: in order to sell the stock on day j, we have to bought it on day[0~j-1]
since we can’t sure which day is the day to buy in, so we have to iterate through every day to get the maximum
so we have to get max(dp[i-1][t-1] + prices[j] - prices[t]) where t from 0 to j-1
核心代码:

for (int i = 1; i <= k; i++) {
            int max = - prices[0]; //initialize max for every row, so max is overall max for each row
            for (int j = 1; j < len; j++) {
                dp[i][j] = Math.max(dp[i][j-1], prices[j] + max); 
                max = Math.max(max, dp[i-1][j-1] - prices[j]); //the j in max is actually j-1
            } //why this could work?
        }
        return dp[k][len - 1];

5 采用双一维DP数组的方式去解

int[] buy = new int[m];//buy[i] means: if we only give i days, then the Maximum profit which end with buying on day i or end with buying on a day before i and takes rest until the day i since then.
int[] sell = new int[m];//sell[i] means: if we only give i days, then the Maximum profit which end with selling on day i or end with selling on a day before i and takes rest until the day i since then.
核心代码:
for (int i = 2; i < prices.length; i++) {
            buy[i] = Math.max(buy[i-1], sell[i-2] - prices[i]); //第i天买的最大利润是从 第i-1天买的最大利润 和 第i-2天卖的最大利润(因为 至少间隔一天) 之间取最大值
            sell[i] = Math.max(sell[i-1], buy[i-1] + prices[i]); //第i天卖的最大利润是从 第i-1天卖的最大利润 和 第i-1天买的最大利润(因为买卖之间无需间隔一天) 之间取最大值
        }
        
        return sell[prices.length - 1]; 

总结一下就是前三道题目差不多的解法
后两道题 转k次需要用二维dp. cooldown一天 需要用两个dp数组。
就硬记吧。

posted @ 2020-11-21 10:12  EvanMeetTheWorld  阅读(26)  评论(0)    收藏  举报