leetcode_[python/C++]_121/122/123/188.Best Time to Buy and Sell Stock I/II/III/IV

121. Best Time to Buy and Sell Stock
[題目]
Say you have an array for which the ith 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.


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).


123. Best Time to Buy and Sell Stock III
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 two transactions.

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


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).


[分析]

首先這四道題是類似的題型,逐步深入

第一道題:求一次買入賣出的最大收益

第二道題:可以買入賣出任意多次,求最大收益

第三道題:限制買入賣出次數最多爲2次,求最大收益

第四道題:限制買入賣出次數最多爲k次,求最大收益


第一道題:
解法1:

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if(size == 0)
        return 0;
        int low = prices[0];
        int high = prices[0];
        int temp_low=low;
        for(int i=1;i<size;i++)
        {
            if(prices[i]-temp_low>=high-low)
            {
                low = temp_low;
                high = prices[i];
            }
            else if(prices[i]>high)
            high = prices[i];
            else if(prices[i]<=low)
            {
                if(prices[i]<=temp_low) temp_low = prices[i];
            }


        }
        if(high-low<=0)
        return 0;
        else
        return high - low;
    }

解法2:

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

或者:

int maxProfit(vector<int>& prices) {
        int minPrice = INT_MAX;
        int maxProfit = 0;

        for (int currPrice : prices){
            if (currPrice < minPrice) minPrice = currPrice;
            else maxProfit = max(currPrice - minPrice, maxProfit);
        }

        return maxProfit;
    }

解法3(動態規劃):

int maxProfit(vector<int>& prices) {
      int Nsize = prices.size();
      int sumCurrent = 0;
      int sumIncrease = 0;

          for(int i = 1; i < Nsize; i++)
          {
              sumIncrease = sumIncrease + prices[i] - prices[i-1];
             if(sumIncrease > sumCurrent) sumCurrent = sumIncrease;
             if(sumIncrease < 0) sumIncrease = 0; 

          }
        return sumCurrent;   
    }

注:第三種解法效率最高


第二道題:
解法1:
從前往後(python):

def maxProfit(self, prices):
        return sum(max(prices[i + 1] - prices[i], 0) for i in range(len(prices) - 1))

從前往後(C++):

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if ( size == 0 || size == 1 ) return 0;
        int low = prices[0] , max_profit = 0;
        for ( int p : prices ){
            if ( p > low ) max_profit += p - low;
            low = p;
        }
        return max_profit;
    }

從前往後2(C++):

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

解法2:
從後往前:

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if ( size == 0 || size == 1 ) return 0;
        int high = prices[size - 1] , max_profit = 0;
        for ( int i = size - 2 ; i >= 0 ; i -- ){
            if ( prices[i] > prices[i+1] ){
                max_profit += high - prices[i+1];
                high = prices[i];
            }
        }
        if ( prices[0] < high ) max_profit += high - prices[0];
        return max_profit;
    }

兩種思路都是O(n) time O(1) space,效率一樣


第三道題:
思路:
找出從0到i的最大收益profit[i]
找出從i到size-1的最大收益profit[i]
之和求最大值


解法1:
O(2n) time O(1) space

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if( size <= 1 ) return 0;
        int profit[size];
        memset(profit,0,sizeof(profit));
        int max_profit = 0;
        int Mins = prices[0];
        for( int i = 1 ; i < size ; i ++ ){
            if( prices[i] - Mins > max_profit ) max_profit = prices[i] - Mins;
            if( prices[i] < Mins ) Mins = prices[i];
            profit[i] = max_profit;
        }
        int max_profit_r = 0;
        int Maxs = prices[size - 1] ;
        for( int i = size - 1 ; i >= 0 ; i-- ){
            if( profit[i] + Maxs - prices[i] > max_profit_r ) max_profit_r = profit[i] + Maxs - prices[i];
            if( Maxs < prices[i] ) Maxs = prices[i];
        }
        return max_profit_r;

    }

解法2:
O(n) time O(1) space

int maxProfit(vector<int>& prices) {
        int size = prices.size();
        if( size <= 1 ) return 0;
        int sel_1 = 0 , sel_2 = 0;
        int buy_1 = -prices[0], buy_2 = -prices[0];
        int profit = 0 ;
        for( int i = 0 ; i < size ; i ++ ) {
            int last_sel = sel_1;
            sel_1 = max( sel_1 , prices[i] + buy_1 );
            buy_1 = max( -prices[i] , buy_1 );
            sel_2 = max( sel_2 , prices[i] + buy_2 );
            buy_2 = max( last_sel - prices[i] , buy_2 );
            profit = max( sel_1 , sel_2 );
        }
        return profit;

    }

注:這兩種解法效率相當


第四道題:
這道題有一個注意的地方就是當k>=prices.size()/2時候,證明我們可以隨意買入賣出,直接累加即可


解法1:
O(kn) space O(n) time

int maxProfit(int k, vector<int>& prices) {
        // f[k, ii] represents the max profit up until prices[ii] (Note: NOT ending with prices[ii]) using at most k transactions. 
        // f[k, ii] = max(f[k, ii-1], prices[ii] - prices[jj] + f[k-1, jj]) { jj in range of [0, ii-1] }
        //          = max(f[k, ii-1], prices[ii] + max(f[k-1, jj] - prices[jj]))
        // f[0, ii] = 0; 0 times transation makes 0 profit
        // f[k, 0] = 0; if there is only one price data point you can't make any money no matter how many times you can trade
        if (prices.size() <= 1) return 0;
        if ( k > prices.size()/2 ){
            int max_profit = 0;
            for( int i = 1 ; i < prices.size() ; i ++ ){
                max_profit += max( prices[i] - prices[i-1] , 0 );
            }
            return max_profit;
        }
        else {

            int maxProf = 0;
            vector<vector<int>> f(k+1, vector<int>(prices.size(), 0));
            for (int kk = 1; kk <= k; kk++) {
                int tmpMax = f[kk-1][0] - prices[0];
                for (int ii = 1; ii < prices.size(); ii++) {
                    f[kk][ii] = max(f[kk][ii-1], prices[ii] + tmpMax);
                    tmpMax = max(tmpMax, f[kk-1][ii] - prices[ii]);
                    maxProf = max(f[kk][ii], maxProf);
                }
            }
            return maxProf;
        }
    }

解法2:
O(kn) space O(n) time
動態規劃(比上面的快一點)

int maxProfit(int k, vector<int>& prices) {
        int n = prices.size();
        if(k == 0 || n <= 1){
            return 0;
        }
        if (k >=  n/2) {
            int maxPro = 0;
            for (int i = 1; i < n; i++) {
                if (prices[i] > prices[i-1])
                    maxPro += prices[i] - prices[i-1];
            }
            return maxPro;
        }
        vector<vector<int>> dp(k + 1, vector<int>(n + 1, 0));
        for(int i = 1; i <= k; ++i){
            int tmpMax = dp[i - 1][0] - prices[0];
            for(int j = 1; j <= n; ++j){
                dp[i][j] = max(dp[i][j - 1], tmpMax + prices[j - 1]);
                tmpMax = max(tmpMax, dp[i - 1][j - 1] - prices[j - 1]);
            }
        }
        return dp[k][n];
    }

解法3:
O(n) space O(n) time
比上面的快點

int maxProfit(int k, vector<int>& prices) {
    int n = prices.size();
    if (k>=n/2) {
        int sum = 0;
        for(int i=1; i<n; i++){
            if(prices[i] > prices[i-1]){
                sum += prices[i] - prices[i-1];
            }
        }
        return sum;
    }
    vector<int> buy(k+1, INT_MIN), sale(k+1, 0);
    for(int i=0; i<n; i++){
        for(int j=1; j<=k; j++){
            buy[j] = max(buy[j], sale[j-1]-prices[i]);
            sale[j] = max(sale[j], buy[j] + prices[i]);
        }
    }
    return sale[k];
}

解法4:
網上的一種解法,利用heap,stack
O(klogn)time ( 最快 )
貼出來一起學習

int maxProfit(int k, vector<int>& prices) {

        // Step 1: Find out all profit opportunities            
        vector<int> profits;
        stack<pair<int, int>> vps; // valley-peak pairs

        int v;
        int p = -1;
        for (;;) {
            // find next valley-peak pair
            for (v = p+1; (v+1 < prices.size()) && (prices[v] >= prices[v+1]); ++v);
            for (p = v  ; (p+1 < prices.size()) && (prices[p] <= prices[p+1]); ++p);

            if (v == p) { // v==p means that both v and p reach the end of the array
                break;
            }

            // Consider two transactions (v1, p1) (back of the stack) and (v2, p2) (the new-found).
            // If prices[v1] >= prices[v2], 
            // it is meaningless to combine the two transactions.
            // Save of profit of (v1, p1), and pop it out of the record.
            while ((!vps.empty()) && (prices[v] <= prices[vps.top().first])) {
                profits.push_back(prices[vps.top().second] - prices[vps.top().first]);
                vps.pop();
            }

            // If prices[v1]<prices[v2] and prices[p1]<prices[p2], 
            // then it is meaningful to combine the two transactions
            // update (v1, p1) to (v1, p2), and save the profit of (v2, p1)
            while ((!vps.empty()) && (prices[p] >= prices[vps.top().second])) {
                profits.push_back(prices[vps.top().second] - prices[v]);
                v = vps.top().first;
                vps.pop();
            }

            // save the new-found valley-peak pair
            vps.emplace(v, p);
        }

        // save all remaining profits
        while (!vps.empty()) {
            profits.push_back(prices[vps.top().second] - prices[vps.top().first]);
            vps.pop();
        }

        // Step 2: Calculate the k highest profits
        int ret;
        if (profits.size() <= k) {
            ret = accumulate(profits.begin(), profits.end(), 0);
        } else {
            nth_element(profits.begin(), profits.end() - k, profits.end());
            ret = accumulate(profits.end() - k, profits.end(), 0);
        }
        return ret;
    }

總結:這四道題雖然不斷變化,但是基本的思想是一致的,而且,基本都是能夠O(n) time的解法解決

發佈了45 篇原創文章 · 獲贊 18 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章