Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions problems/1840-maximum-building-height/analysis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# 1840. Maximum Building Height

[LeetCode Link](https://leetcode.com/problems/maximum-building-height/)

Difficulty: Hard
Topics: Array, Math, Sorting
Acceptance Rate: 50.1%

## Hints

### Hint 1

`n` can be up to `10^9`, so you cannot build an array of heights and simulate every building. Notice that the number of *restrictions* is small (at most `10^5`). The interesting events only happen at the restricted buildings (plus building `1`, which is fixed at height `0`). Think about how to reason only about those "anchor" points and what happens *between* them.

### Hint 2

A restriction does not act in isolation. Because adjacent buildings differ by at most `1`, a low cap on one building forces nearby buildings to be low too. Sort the restrictions by index and ask: given building `a` is capped at height `Ha`, what is the tightest cap that propagates to a later building `b`? This is a relaxation/propagation idea — sweep left-to-right, then right-to-left, tightening each cap from its neighbors.

### Hint 3

Once every anchor has its *final, fully-propagated* maximum height, consider a single gap between two consecutive anchors `(idA, hA)` and `(idB, hB)`. Heights can climb up from both sides and meet at a peak in the middle. With `gap = idB - idA`, the peak you can reach is `(hA + hB + gap) / 2` (integer division — slopes of `+1` from each side meeting in the middle). The answer is the maximum peak over all consecutive gaps.

## Approach

We treat the problem as constraint propagation over a small set of "anchor" buildings.

**Step 1 — Collect anchors.**
The only buildings that matter are the restricted ones, building `1` (forced to height `0`), and building `n` (it can be the tallest, climbing freely on the right). Build a list of `(id, maxHeight)` points:

- Add `(1, 0)` — building 1 is forced to `0`.
- Keep every given restriction.
- If building `n` is not already restricted, add `(n, n-1)`. The cap `n-1` is the largest height physically reachable at index `n` (starting from `0` and rising by at most `1` per step), so it never over-constrains; it just gives us an anchor on the right edge.

Sort all anchors by `id`.

**Step 2 — Forward propagation (left to right).**
A building can be at most `1` taller than its left neighbor for each step between them. So for consecutive anchors `i-1` and `i`:

```
h[i] = min(h[i], h[i-1] + (id[i] - id[i-1]))
```

This pushes the unavoidable consequences of low caps (and the forced `0` at building 1) rightward.

**Step 3 — Backward propagation (right to left).**
Symmetrically, a cap on a later building limits earlier ones:

```
h[i] = min(h[i], h[i+1] + (id[i+1] - id[i]))
```

After both passes, every anchor holds its true maximum achievable height.

**Step 4 — Find the peak in each gap.**
Between two adjacent anchors `(idA, hA)` and `(idB, hB)` with `gap = idB - idA`, heights rise with slope `+1` from each side. The tallest point in the gap is:

```
peak = (hA + hB + gap) / 2 // integer division
```

The overall answer is the maximum of these peaks across all consecutive pairs. The anchor heights themselves are included automatically because a pair where one side is the taller anchor still yields at least that height.

**Worked example (Example 1):** `n = 5`, `restrictions = [[2,1],[4,1]]`.
Anchors after adding `(1,0)` and `(5,4)`: `[(1,0),(2,1),(4,1),(5,4)]`.
Forward pass leaves them `[0,1,1,2]`; backward pass keeps them `[0,1,1,2]`.
Gaps give peaks `1`, `(1+1+2)/2 = 2`, `(1+2+1)/2 = 2`. Maximum is **2**. ✅

This problem is genuinely a Hard — the propagation-both-ways plus the meet-in-the-middle peak formula is the crux. If you derived the `(hA + hB + gap) / 2` idea on your own, that is the hardest part done.

## Complexity Analysis

Let `m` be the number of restrictions.

Time Complexity: O(m log m) — dominated by sorting the anchors. The two propagation passes and the peak scan are each O(m).
Space Complexity: O(m) — for the list of anchor points.

## Edge Cases

- **No restrictions (`restrictions = []`):** Only anchors are `(1,0)` and the added `(n, n-1)`. The peak formula yields `n-1`, the unrestricted maximum (Example 2).
- **Building `n` already restricted:** Do not add a synthetic `(n, n-1)`; use the real cap, otherwise you would ignore a genuine constraint (Example 3 has `(10,3)`).
- **A restriction with `maxHeight` larger than reachable:** Forward/backward propagation tightens it down to what is physically achievable, so an overly generous cap never inflates the answer.
- **Out-of-order restrictions in the input:** Always sort by `id` first; the propagation logic assumes ascending indices.
- **Integer division in the peak formula:** `(hA + hB + gap) / 2` must use floor division — when the meeting point falls between two buildings, the achievable integer height is the floor.
- **Large `n` (up to `10^9`):** Never allocate per-building storage; everything is keyed off the `O(m)` anchors. Heights and indices fit comfortably in Go's `int` (64-bit on common platforms).
79 changes: 79 additions & 0 deletions problems/1840-maximum-building-height/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
number: "1840"
frontend_id: "1840"
title: "Maximum Building Height"
slug: "maximum-building-height"
difficulty: "Hard"
topics:
- "Array"
- "Math"
- "Sorting"
acceptance_rate: 5014.0
is_premium: false
created_at: "2026-06-20T04:49:28.135969+00:00"
fetched_at: "2026-06-20T04:49:28.135969+00:00"
link: "https://leetcode.com/problems/maximum-building-height/"
date: "2026-06-20"
---

# 1840. Maximum Building Height

You want to build `n` new buildings in a city. The new buildings will be built in a line and are labeled from `1` to `n`.

However, there are city restrictions on the heights of the new buildings:

* The height of each building must be a non-negative integer.
* The height of the first building **must** be `0`.
* The height difference between any two adjacent buildings **cannot exceed** `1`.



Additionally, there are city restrictions on the maximum height of specific buildings. These restrictions are given as a 2D integer array `restrictions` where `restrictions[i] = [idi, maxHeighti]` indicates that building `idi` must have a height **less than or equal to** `maxHeighti`.

It is guaranteed that each building will appear **at most once** in `restrictions`, and building `1` will **not** be in `restrictions`.

Return _the**maximum possible height** of the **tallest** building_.



**Example 1:**

![](https://assets.leetcode.com/uploads/2021/04/08/ic236-q4-ex1-1.png)


**Input:** n = 5, restrictions = [[2,1],[4,1]]
**Output:** 2
**Explanation:** The green area in the image indicates the maximum allowed height for each building.
We can build the buildings with heights [0,1,2,1,2], and the tallest building has a height of 2.

**Example 2:**

![](https://assets.leetcode.com/uploads/2021/04/08/ic236-q4-ex2.png)


**Input:** n = 6, restrictions = []
**Output:** 5
**Explanation:** The green area in the image indicates the maximum allowed height for each building.
We can build the buildings with heights [0,1,2,3,4,5], and the tallest building has a height of 5.


**Example 3:**

![](https://assets.leetcode.com/uploads/2021/04/08/ic236-q4-ex3.png)


**Input:** n = 10, restrictions = [[5,3],[2,5],[7,4],[10,3]]
**Output:** 5
**Explanation:** The green area in the image indicates the maximum allowed height for each building.
We can build the buildings with heights [0,1,2,3,3,4,4,5,4,3], and the tallest building has a height of 5.




**Constraints:**

* `2 <= n <= 109`
* `0 <= restrictions.length <= min(n - 1, 105)`
* `2 <= idi <= n`
* `idi` is **unique**.
* `0 <= maxHeighti <= 109`
72 changes: 72 additions & 0 deletions problems/1840-maximum-building-height/solution.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

// 1840. Maximum Building Height
//
// Approach: constraint propagation over "anchor" buildings.
//
// n can be up to 1e9, so we never materialize all heights. Only the restricted
// buildings, building 1 (forced to height 0), and building n (which can be the
// tallest) matter. We:
// 1. Build anchor points (id, maxHeight), adding (1, 0) and, if missing,
// (n, n-1) — the largest height physically reachable at index n.
// 2. Sort by id, then propagate caps left-to-right and right-to-left so each
// anchor holds its true maximum achievable height (adjacent buildings differ
// by at most 1).
// 3. For each gap between consecutive anchors, heights rise with slope +1 from
// both sides and meet at peak = (hA + hB + gap) / 2. The answer is the max
// peak over all gaps.
//
// Time: O(m log m) for m restrictions (sorting dominates).
// Space: O(m).

import "sort"

func maxBuilding(n int, restrictions [][]int) int {
// Build anchor list with fresh inner slices so we never mutate the input.
anchors := make([][2]int, 0, len(restrictions)+2)
anchors = append(anchors, [2]int{1, 0}) // building 1 is forced to height 0
hasN := false
for _, r := range restrictions {
anchors = append(anchors, [2]int{r[0], r[1]})
if r[0] == n {
hasN = true
}
}
if !hasN {
// n-1 is the tallest height index n could ever reach; never over-constrains.
anchors = append(anchors, [2]int{n, n - 1})
}

sort.Slice(anchors, func(i, j int) bool {
return anchors[i][0] < anchors[j][0]
})

m := len(anchors)

// Forward propagation: a building is at most 1 taller per step than its left neighbor.
for i := 1; i < m; i++ {
diff := anchors[i][0] - anchors[i-1][0]
if cap := anchors[i-1][1] + diff; cap < anchors[i][1] {
anchors[i][1] = cap
}
}

// Backward propagation: a later cap limits earlier buildings too.
for i := m - 2; i >= 0; i-- {
diff := anchors[i+1][0] - anchors[i][0]
if cap := anchors[i+1][1] + diff; cap < anchors[i][1] {
anchors[i][1] = cap
}
}

// Meet-in-the-middle peak for every gap between consecutive anchors.
ans := 0
for i := 1; i < m; i++ {
gap := anchors[i][0] - anchors[i-1][0]
peak := (anchors[i-1][1] + anchors[i][1] + gap) / 2
if peak > ans {
ans = peak
}
}
return ans
}
64 changes: 64 additions & 0 deletions problems/1840-maximum-building-height/solution_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import "testing"

func TestSolution(t *testing.T) {
tests := []struct {
name string
n int
restrictions [][]int
expected int
}{
{
name: "example 1: caps at buildings 2 and 4",
n: 5,
restrictions: [][]int{{2, 1}, {4, 1}},
expected: 2,
},
{
name: "example 2: no restrictions, climb freely",
n: 6,
restrictions: [][]int{},
expected: 5,
},
{
name: "example 3: unsorted restrictions with cap at n",
n: 10,
restrictions: [][]int{{5, 3}, {2, 5}, {7, 4}, {10, 3}},
expected: 5,
},
{
name: "edge case: smallest n with no restrictions",
n: 2,
restrictions: [][]int{},
expected: 1,
},
{
name: "edge case: building n hard-capped at zero",
n: 5,
restrictions: [][]int{{5, 0}},
expected: 2,
},
{
name: "edge case: generous cap tightened by propagation",
n: 5,
restrictions: [][]int{{3, 0}},
expected: 2,
},
{
name: "edge case: large n unrestricted",
n: 1000000000,
restrictions: [][]int{},
expected: 999999999,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := maxBuilding(tt.n, tt.restrictions)
if result != tt.expected {
t.Errorf("maxBuilding(%d, %v) = %d, want %d", tt.n, tt.restrictions, result, tt.expected)
}
})
}
}