引言
現在算法題中,有時會遇到求解絕對值最值的問題,比如給定一個數組,求解的最大值。諸如此類問題,暴力解法是用時間複雜度遍歷。而一種常用優化是將絕對值拆開,比如,將問題拆成兩個子問題,而每個子問題通過維護當前最小值,即的時間可解出。最後通過取二者最值即可。
下面分享三道利用此技巧的算法題,難度按升序排列。
Leetcode 1131
題意:
給定兩個數組和,讓找兩個下標和,使得最大。
思路:
這裏直接拆開三個絕對值,得到以下8個子問題:
然後我們把有關於下標和下標的項提出來放在一起,發現其實子問題只有以下四個:1&8;2&7;3&6;4&5(比如1和8,它們只不過把兩個下標和順序調換了下)。
而這四個子問題的差別就是它們中的符號爲加減法的排列組合:; ; ; 。這樣通過四個的循環即可求出最優解。
代碼:
const int INF = 0x3f3f3f3f;
class Solution {
public:
int maxAbsValExpr(vector<int>& x, vector<int>& y) {
int res = 0, n = x.size();
for (int sign1=-1; sign1<=1; sign1+=2)
for (int sign2=-1; sign2<=1; sign2+=2) {
int mx = -INF, mn = INF;
for (int i=0; i<n; i++) {
int val = x[i] + sign1*y[i] + sign2*i;
mx = max(mx, val);
mn = min(mn, val);
}
res = max(res, mx - mn);
}
return res;
}
};
Leetcode 1330
題意:
給定一個數組,它的值被定義爲所有的和(其中)。我們有一個操作,能使它某一段連續的子數組翻轉一次,問翻轉前/後這個數組的值最大能爲多少?
思路:
- 如果我們選擇不翻轉子數組,那麼它的值可以通過時間遍歷得到。這個值我們記錄爲。
- 如果我們翻轉子數組,這時新數組的爲:
,這裏是不變的就可以不管它,然後又出現了絕對值最值問題。
此時我們只展開前兩個絕對值,因爲展開絕對值的目的是把與相關的放一起,與相關的放一起,而後兩個絕對值裏只包含了。
展開後,得到這二者:
1.
2.
最後,取二者最大值即可。
代碼:
class Solution {
public:
int solve(const vector<int>& A) {
int n = A.size(), base = 0;
for (int i=0; i<n-1; i++)
base += abs(A[i] - A[i+1]);
int inc = 0;
for (int sign=-1; sign<=1; sign+=2) {
int mnVal = A[0] + sign*A[1] + abs(A[0]-A[1]);
for (int i=1; i<n-1; i++) {
int biggerVal = A[i] + sign*A[i+1] - abs(A[i]-A[i+1]);
inc = max(inc, biggerVal - mnVal);
mnVal = min(mnVal, A[i] + sign*A[i+1] + abs(A[i]-A[i+1]));
}
}
return base + inc;
}
int maxValueAfterReverse(vector<int>& nums) {
if (nums.size() <= 1) return 0;
int res1 = solve(nums);
reverse(nums.begin(), nums.end());
int res2 = solve(nums);
return max(res1, res2);
}
};
Google Kick Start Round A 2019 - Parcels
題意:
給定一個的01地圖,標記爲1的位置爲快遞站點,標記爲0的位置爲收快遞的區域。假如我們站在某個0位置,那麼送快遞的時間爲距離我最近的快遞站到我當前位置的曼哈頓距離。爲了減小送快遞時間,我們可以在某個0的位置新建一個快遞站點,問建立完後大家收快遞所需要的最長時間最快是多少?
思路:
這個題暴力解法是,對於每個潛在位置,都試圖建立一個快遞站,然後BFS求一遍最長時間,時間複雜度是。
由於新建快遞站點的位置很難確定,我們可以先用二分法把最優化問題轉化爲判定性問題,即,給定一個最長時間,我們能否找到新增一個新快遞站,使得所有快遞的運輸時間都小於等於?
那麼我們二分枚舉的值,然後對於某個,遍歷一邊整個地圖找到所有運輸時間大於的位置,記錄下來(假設有個,)。現在問題存不存在一個新快遞快遞站點,使得這些位置的運輸時間小於T。
這裏曼哈頓距離是絕對值形式表示的,於是兩點之間的距離可以被表示爲:。這裏假設爲快遞點的位置,爲運輸時間大於的位置,我們遍可以利用的方式求出和的最值,然後再遍歷一邊地圖檢驗是否存在這一快遞點即可。
這種解法利用了二分和絕對值優化,它總時間複雜度爲。本題還有一個的方法,在此不做介紹(不會…)。
關鍵代碼:
bool ok(int K) {
vector<pair<int, int> > focusBlanks;
int mx1 = -INF, mn1 = INF, mx2 = -INF, mn2 = INF;
for (int i=0; i<row; i++) {
for (int j=0; j<col; j++) if (dis[i][j] > K) {
focusBlanks.push_back(make_pair(i, j));
mx1 = max(mx1, i + j); mn1 = min(mn1, i + j);
mx2 = max(mx2, i - j); mn2 = min(mn2, i - j);
}
}
if (focusBlanks.size() == 0) return true;
for (int i=0; i<blanks.size(); i++) {
int x2 = blanks[i].first, y2 = blanks[i].second;
int dis1 = max(abs(mx1 - (x2 + y2)), abs(mn1 - (x2 + y2)));
int dis2 = max(abs(mx2 - (x2 - y2)), abs(mn2 - (x2 - y2)));
if (max(dis1, dis2) <= K) return true;
}
// printf("%d : %d\n", K, false);
return false;
}