題目描述:給定陣列 maxHeights,代表每個位置可建塔的最大高度。需要建造一排塔,使高度序列形成「山形」(先非遞減後非遞增),且每座塔高度不超過對應的 maxHeights[i]。求所有塔高度總和的最大值。
解題思路:枚舉每個位置 m 作為山頂,山頂高度固定為 maxHeights[m]。利用單調棧分別計算:
前綴貢獻和 prefix[i]:從位置 0 到 i,以 i 為山頂或山坡最右端時,左側最優排列的高度總和。維護單調遞增棧(存 (height, index) 對),當遇到新位置 i 且 maxHeights[i] 小於棧頂高度時,持續彈出,表示棧頂位置受到 i 的高度限制。更新公式:prefix[i] = prefix[stack.top()] + maxHeights[i] * (i - stack.top()),意即從棧頂的下一位置到 i,全部填 maxHeights[i] 高度。
後綴貢獻和 suffix[i]:從右向左同理計算,以 i 為起點向右的最優高度總和。
最終答案:枚舉所有山頂 m,答案 = max(prefix[m] + suffix[m] - maxHeights[m])(因為 m 被計算了兩次,需減去一次)。
時間複雜度:O(n) — 兩次線性掃描,每個元素最多入棧、出棧各一次,故前綴與後綴各 O(n),枚舉山頂 O(n),整體 O(n)。
空間複雜度:O(n) — prefix、suffix 陣列各 O(n),單調棧最多存 n 個元素,故空間 O(n)。
Function maximumSumOfHeights(maxHeights):
n = length of maxHeights
prefix[0..n-1] = 0, suffix[0..n-1] = 0
// Step 1: Compute prefix using monotone increasing stack (left to right)
stack = empty
For i from 0 to n-1:
While stack not empty AND maxHeights[stack.top] > maxHeights[i]:
Pop stack
If stack is empty:
prefix[i] = maxHeights[i] * (i + 1)
Else:
j = stack.top
prefix[i] = prefix[j] + maxHeights[i] * (i - j)
Push i onto stack
// Step 2: Compute suffix using monotone increasing stack (right to left)
stack = empty
For i from n-1 down to 0:
While stack not empty AND maxHeights[stack.top] > maxHeights[i]:
Pop stack
If stack is empty:
suffix[i] = maxHeights[i] * (n - i)
Else:
j = stack.top
suffix[i] = suffix[j] + maxHeights[i] * (j - i)
Push i onto stack
// Step 3: Enumerate peak and find maximum
ans = 0
For m from 0 to n-1:
ans = max(ans, prefix[m] + suffix[m] - maxHeights[m])
Return ans暴力枚舉 O(n²):對每個山頂 m,線性掃描左側與右側計算最大合法高度總和。邏輯直觀,但 n 達 10⁵ 時會超時。
分段處理(同一單調棧一次遍歷):有些實作將前綴與後綴合併到同一趟掃描中維護,可減少常數,但邏輯較複雜,不易理解與除錯,競賽外通常不推薦。
線段樹 / 稀疏表:可用 RMQ(區間最小值查詢)結合二分搜尋快速定位每個位置的「受限邊界」,時間複雜度同為 O(n log n),但常數比單調棧大,且程式碼量明顯增加。