Beautiful Towers I

medium monotonic stack array prefix sums

Problem

Given heights of n towers, you may lower any tower (remove bricks) but never raise one. Reshape them into a mountain: heights are non-decreasing up to a peak (one or more equal towers) and then non-increasing. Return the maximum possible sum of the resulting heights.

Inputheights = [5, 3, 4, 1, 1]
Output13
Lower index 2 to make [5, 3, 3, 1, 1]; the peak sits at index 0. Sum = 5+3+3+1+1 = 13.

def maximumSumOfHeights(heights):
    n = len(heights)
    # prefix[i]: best sum of a non-decreasing run that peaks at i
    prefix = [0] * n
    stack, s = [], 0                  # stack of plateau indices, s = running sum
    for i in range(n):
        # lower every taller tower on the left down to heights[i]
        while stack and heights[stack[-1]] > heights[i]:
            j = stack.pop()
            width = j - (stack[-1] if stack else -1)
            s -= width * heights[j]
        width = i - (stack[-1] if stack else -1)
        s += width * heights[i]       # heights[i] now spans that plateau
        stack.append(i)
        prefix[i] = s
    # suffix[i]: best sum of a non-increasing run that peaks at i (mirror)
    suffix = [0] * n
    stack, s = [], 0
    for i in range(n - 1, -1, -1):
        while stack and heights[stack[-1]] > heights[i]:
            j = stack.pop()
            width = (stack[-1] if stack else n) - j
            s -= width * heights[j]
        width = (stack[-1] if stack else n) - i
        s += width * heights[i]
        stack.append(i)
        suffix[i] = s
    # join the two halves at each peak (count the peak once)
    return max(prefix[i] + suffix[i] - heights[i] for i in range(n))
function maximumSumOfHeights(heights) {
  const n = heights.length;
  // prefix[i]: best sum of a non-decreasing run that peaks at i
  const prefix = new Array(n).fill(0);
  let stack = [], s = 0;             // stack of plateau indices
  for (let i = 0; i < n; i++) {
    // lower every taller tower on the left down to heights[i]
    while (stack.length && heights[stack[stack.length - 1]] > heights[i]) {
      const j = stack.pop();
      const width = j - (stack.length ? stack[stack.length - 1] : -1);
      s -= width * heights[j];
    }
    const width = i - (stack.length ? stack[stack.length - 1] : -1);
    s += width * heights[i];          // heights[i] spans that plateau
    stack.push(i);
    prefix[i] = s;
  }
  // suffix[i]: best sum of a non-increasing run that peaks at i (mirror)
  const suffix = new Array(n).fill(0);
  stack = []; s = 0;
  for (let i = n - 1; i >= 0; i--) {
    while (stack.length && heights[stack[stack.length - 1]] > heights[i]) {
      const j = stack.pop();
      const width = (stack.length ? stack[stack.length - 1] : n) - j;
      s -= width * heights[j];
    }
    const width = (stack.length ? stack[stack.length - 1] : n) - i;
    s += width * heights[i];
    stack.push(i);
    suffix[i] = s;
  }
  // join the two halves at each peak (count the peak once)
  let best = 0;
  for (let i = 0; i < n; i++) best = Math.max(best, prefix[i] + suffix[i] - heights[i]);
  return best;
}
long maximumSumOfHeights(int[] heights) {
    int n = heights.length;
    // prefix[i]: best sum of a non-decreasing run that peaks at i
    long[] prefix = new long[n];
    Deque<Integer> stack = new ArrayDeque<>();
    long s = 0;                       // running sum of the plateau model
    for (int i = 0; i < n; i++) {
        // lower every taller tower on the left down to heights[i]
        while (!stack.isEmpty() && heights[stack.peek()] > heights[i]) {
            int j = stack.pop();
            long width = j - (stack.isEmpty() ? -1 : stack.peek());
            s -= width * heights[j];
        }
        long width = i - (stack.isEmpty() ? -1 : stack.peek());
        s += width * heights[i];
        stack.push(i);
        prefix[i] = s;
    }
    // suffix[i]: best sum of a non-increasing run that peaks at i (mirror)
    long[] suffix = new long[n];
    stack.clear(); s = 0;
    for (int i = n - 1; i >= 0; i--) {
        while (!stack.isEmpty() && heights[stack.peek()] > heights[i]) {
            int j = stack.pop();
            long width = (stack.isEmpty() ? n : stack.peek()) - j;
            s -= width * heights[j];
        }
        long width = (stack.isEmpty() ? n : stack.peek()) - i;
        s += width * heights[i];
        stack.push(i);
        suffix[i] = s;
    }
    long best = 0;                    // join the two halves at each peak
    for (int i = 0; i < n; i++) best = Math.max(best, prefix[i] + suffix[i] - heights[i]);
    return best;
}
long long maximumSumOfHeights(vector<int>& heights) {
    int n = heights.size();
    // prefix[i]: best sum of a non-decreasing run that peaks at i
    vector<long long> prefix(n, 0);
    vector<int> stack;
    long long s = 0;                  // running sum of the plateau model
    for (int i = 0; i < n; i++) {
        // lower every taller tower on the left down to heights[i]
        while (!stack.empty() && heights[stack.back()] > heights[i]) {
            int j = stack.back(); stack.pop_back();
            long long width = j - (stack.empty() ? -1 : stack.back());
            s -= width * heights[j];
        }
        long long width = i - (stack.empty() ? -1 : stack.back());
        s += width * heights[i];
        stack.push_back(i);
        prefix[i] = s;
    }
    // suffix[i]: best sum of a non-increasing run that peaks at i (mirror)
    vector<long long> suffix(n, 0);
    stack.clear(); s = 0;
    for (int i = n - 1; i >= 0; i--) {
        while (!stack.empty() && heights[stack.back()] > heights[i]) {
            int j = stack.back(); stack.pop_back();
            long long width = (stack.empty() ? n : stack.back()) - j;
            s -= width * heights[j];
        }
        long long width = (stack.empty() ? n : stack.back()) - i;
        s += width * heights[i];
        stack.push_back(i);
        suffix[i] = s;
    }
    long long best = 0;               // join the two halves at each peak
    for (int i = 0; i < n; i++) best = max(best, prefix[i] + suffix[i] - heights[i]);
    return best;
}
Time: O(n) Space: O(n)