Leetcode 121
題目與分析
題目大意是有一個股票價格序列,找到最優的買進賣出時間點,算出最高差價。O(N)時間可以找到最低買入點和最大差價。
雖然是很簡單的一道題,但這個解題的範式是非常典型的。
基本範式:
res = A[j] - A[i], i < j
find Max(res)
如果沒有的限制,那就是一次遍歷找到最大最小值做差值就可以了。
有了下標的限制之後,下標限制中小的那一項照舊更新,另一項的更新改爲直接更新目標式。即,最小值的更新如舊,最大值的更新改爲最大查值的更新。
.
代碼
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
題目與分析
題目大意是,挑選最優的景點組合,組合的評分標準是兩個景點的分數相加,再減去兩景點間的距離。
說得這麼複雜,其實跟上面一道題本質是一樣的。
歸納到基本範式:
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
題目與分析
這是Leetcode146周賽的第四題,當時看到覺得很眼熟,但沒來得及做(啊,我好菜orz
後來看評論區大神的代碼,覺得大神代碼雖然寫得很好,但那個解釋就很奇怪。後來想起來,跟前面兩道題其實是同類題。
但要把這道題歸納到Leetcode121的基本範式有2個問題:
- 沒有的限制,題目只說了 ,沒有定義i,j之間的關係
- 絕對值怎麼處理
如果先無視這兩個問題,無腦歸納一下,歸納到基本範式:
res = (A1[j] + A2[j] + j) - (A1[i] + A2[i] + i), i < j
find Max(res)
看上去是不是就很簡單了!
我們再來解決上面提到的那兩個問題:
-
的限制
如果設,由題意不難得知,所以不妨 人爲規定 ,這樣不僅解決了i,j大小關係的限制,同時去掉了第三個絕對值。現在目標式變成:
-
絕對值的處理
回憶剛開始學習絕對值時,老師是怎麼教的:如果絕對值符號內的數值大於0,去掉絕對值,內容不變;如果絕對值符號內的數值小於0,去掉絕對值,在式子前面添加負號。
1個絕對值是2種情況,2個絕對值就是4種情況:正正、正負、負正、負負。
沒錯就是這麼簡單!不用費心去判斷每組到底是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個特點:
- 目標式可以拆成兩部分:;
- 有下標限制:;
- O(N)更新下標限制中小的那一項—— 和目標式
.
其他
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;
}
};