【算法】Leetcode121股票買賣 & 1014最佳觀光組合 & 1131 —— 同思路從易到難

Leetcode 121


題目與分析

Leetcode 121 題目
題目大意是有一個股票價格序列,找到最優的買進賣出時間點,算出最高差價。O(N)時間可以找到最低買入點和最大差價。
雖然是很簡單的一道題,但這個解題的範式是非常典型的。

基本範式:

res = A[j] - A[i], i < j
find Max(res)

如果沒有i&lt;ji &lt; j的限制,那就是一次遍歷找到最大最小值做差值就可以了。
有了下標的限制之後,下標限制中小的那一項照舊更新,另一項的更新改爲直接更新目標式。即,最小值的更新如舊,最大值的更新改爲最大查值的更新。
.

代碼

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if(prices.empty())
            return 0;
        int res = 0, buy = prices[0];
        for(int i = 0; i < prices.size(); i++){
            res = max(res, prices[i] - buy);  // 目標式的更新
            buy = min(buy, prices[i]);        // 第二項的更新
        }
        return res;
    }
};

.

Leetcode 1014


題目與分析

Leetcode1014題目
題目大意是,挑選最優的景點組合,組合的評分標準是兩個景點的分數相加,再減去兩景點間的距離。
說得這麼複雜,其實跟上面一道題本質是一樣的。

歸納到基本範式:

res = (A[j] - j) + (A[i] + i), i < j
find Max(res)

區別在於,中間是+連接,那麼代碼中的第二項本來用min更新,這裏就改用max做更新。
.

代碼

class Solution {
public:
    int maxScoreSightseeingPair(vector<int>& A) {
        int res = 0, max_i = A[0]; // A[0] + 0;
        for(int j = 1; j < A.size(); j++){
            res = max(res, max_i + A[j] - j);  // 目標式的更新
            max_i = max(max_i, A[j] + j);      // 第二項的更新
        }
        return res;
    }
};

.

Leetcode 1131


題目與分析

Leetcode1131題目
這是Leetcode146周賽的第四題,當時看到覺得很眼熟,但沒來得及做(啊,我好菜orz
後來看評論區大神的代碼,覺得大神代碼雖然寫得很好,但那個解釋就很奇怪。後來想起來,跟前面兩道題其實是同類題。

但要把這道題歸納到Leetcode121的基本範式有2個問題:

  • 沒有i&lt;ji&lt;j的限制,題目只說了 0i,j&lt;arr1.length0\leq i,j &lt; arr1.length,沒有定義i,j之間的關係
  • 絕對值怎麼處理

如果先無視這兩個問題,無腦歸納一下,歸納到基本範式:

res = (A1[j] + A2[j] + j) - (A1[i] + A2[i] + i), i < j
find Max(res)

看上去是不是就很簡單了!

我們再來解決上面提到的那兩個問題

  1. i&lt;ji&lt;j的限制
    如果設f(i,j)=arr1[i]arr1[j]+arr2[i]arr2[j]+ijf(i,j)=|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j|,由題意不難得知f(i,j)=f(j,i)f(i,j) = f(j,i),所以不妨 人爲規定 i&lt;ji&lt;j,這樣不僅解決了i,j大小關係的限制,同時去掉了第三個絕對值。現在目標式變成:
    arr1[i]arr1[j]+arr2[i]arr2[j]+ji|arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + j - i

  2. 絕對值的處理
    回憶剛開始學習絕對值時,老師是怎麼教的:如果絕對值符號內的數值大於0,去掉絕對值,內容不變;如果絕對值符號內的數值小於0,去掉絕對值,在式子前面添加負號。
    1個絕對值是2種情況,2個絕對值就是4種情況:正正、正負、負正、負負。
    沒錯就是這麼簡單!不用費心去判斷每組i,ji,j到底是4種情況的哪種,只要遍歷4遍就可以了。

改進後的基本範式:

p,q = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}
res = (p*(A1[j] + A2[j]) + j)  -  (q*(A1[i] + A2[i]) + i), i < j
find Max(res)

再解釋一下,爲什麼可以無腦拆絕對值:因爲最終求的是最大值,如果絕對值拆錯了,比如去掉絕對值應該取負的沒有取,去掉絕對值不需要取負的取負了,只會導致計算的結果變小,所以在對目標式取max的時候,是不會取到錯誤的計算結果的。
.

代碼

class Solution {
public:
	int maxAbsValExpr(vector<int>& arr1, vector<int>& arr2) {
		int res = 0;
		for (int p : {1, -1}) {
			for (int q : {1, -1}) {
				int min_j = p * arr1[0] + q * arr2[0] + 0;
				for (int i = 0; i < arr1.size(); i++) {
					int tmp = p * arr1[i] + q * arr2[i] + i;
					res = max(res, tmp - min_j);   // 目標式的更新
					min_j = min(tmp, min_j);       // 第二項的更新
				}
			}
		}
		return res;
	}
};

.

總結


這類範式的3個特點:

  • 目標式可以拆成兩部分:f(i,j)=f1(i)+f2(j)f(i,j) =f_1(i)+f_2(j)
  • 有下標限制:i&lt;ji&lt;j
  • O(N)更新下標限制中小的那一項——f1(i)f_1(i) 和目標式f(i,j)f(i,j)

.

其他


Leetcode121 一個更優雅的寫法:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int res = 0, buy = INT_MAX;
        for (int price : prices) {
            buy = min(buy, price);
            res = max(res, price - buy);
        }
        return res;
    }
};

Leetcode1014 一個加速技巧:

class Solution {
public:
    Solution(){
        std::ios::sync_with_stdio(0);
        cin.tie(0);
    }
    int maxScoreSightseeingPair(vector<int>& A) {
        int res = 0, max_i = A[0]; // A[0] + 0;
        for(int j = 1; j < A.size(); j++){
            res = max(res, max_i + A[j] - j); 
            max_i = max(max_i, A[j] + j);
        }
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章