題目出處
給字n個非負整數, a1,a2, … , an, 對應着座標軸(i, ai), 對每個數連接x軸畫一條線段,端點 (i, ai) 和 (i, 0).
對兩個數,連接端點, 再與x軸形成長方形, 求長方形的面積就想關於盛水的體積, 求最大的盛水量。
如假設給定的10個數:
[5, 4, 3, 10, 2, 7, 8, 6, 1, 9],即10 個點 (0, 5), (1, 4), (2, 3), (3, 10), … ,(9, 9) 對應的直觀圖如下
藍色區域代碼的是 (0, 5),(3, 10)兩點的盛水量爲: 5*3 = 15.
相應的, 灰色區域代碼的是 (3 10),(5, 7)兩點的盛水量爲: 7*(5 -3) = 14.
相應的, 黃色區域代碼的是 (6, 8),(9, 9)兩點的盛水量爲: 8*(9 -6) = 24.
上圖數據中,最大值爲(3, 10) , (9, 9), 盛水量爲: 9 * (9-3) = 54
分析
最直接的求法是計算兩兩計算一遍,求最大值。
但是明顯的,有很多沒必要的運算. 比如上圖中的(1, 4), (2, 3) 與(10, 10) 的計算。
由於這是計算盛水量,所以主要取決於較小的數據,所以最大的數據再大,也沒有起作用。
考慮到一般情況:
兩個數字(i, ai), (j, aj), 他們的盛水量爲 area =(j-i)*min(ai, aj), 取(k, ak) 使 i < k < j, 使面積比area更大。
不失一般性,設 ai <= aj,則min(ai, aj) = ai.
當ak <= ai 時, 由於 (k-i) * ak < area 和 (j-k) * ak < area(由於 k-i < j-i 和 j-k < j-i), 所以這種情況得到的結果肯定小於 area. 可以不考慮。
當 ak > ai 時
ak 與ai的盛水量: (k-i)*ai < area, 所以這種情況也不需要考慮。
ak與aj的盛水量: (j-k)*min(ak, aj) 由於 j-k < j-i 而 min(ak, aj) > ai, 不確定是否大於area.
所以可得到簡單算法:
- 取第一和最後的值爲初始值,計算最近 盛水量。
- 往中間移動較小值的指針,錄找比它大的數。
- 再計算盛水量,並比較大小。
- 重複2, 3.
算法代碼
int maxArea(vector<int>& height) {
int first = 0, end = height.size()-1;
int min = height[first] > height[end] ? end : first;
int maxArea = (end-first)*height[min];
while(first < end){
// cout<<first << " " << end << " min:" << min << endl;
// 移動最小值到下一個比當前最小值大的數。
if(first == min){
while(first < end && height[first] <= height[min]) first += 1;
}
if(end == min){
while(first < end && height[end] <= height[min]) end -= 1;
}
// 沒有就退出
if(first >= end ) break;
// 計算盛水量比較
min = height[first] < height[end] ? first : end;
int area = (end-first) * height[min];
if(area > maxArea)
maxArea = area;
}
return maxArea;
}