LeetCode 1526 - Minimum Number of Increments on Subarrays to Form a Target Array
The problem gives us a target array and asks us to build it starting from an array of all zeros. The only operation allowed is selecting any contiguous subarray and incrementing every element in that subarray by exactly one.
Difficulty: 🔴 Hard
Topics: Array, Dynamic Programming, Stack, Greedy, Monotonic Stack
Solution
Problem Understanding
The problem gives us a target array and asks us to build it starting from an array of all zeros. The only operation allowed is selecting any contiguous subarray and incrementing every element in that subarray by exactly one.
The goal is to determine the minimum number of such operations required to transform the zero array into the target array.
For example, if the target is [1,2,3,2,1], we can think of the array as a landscape of heights. Each operation adds one horizontal layer across some contiguous region. The challenge is to cover the landscape using the fewest layers possible.
The input consists of:
- An integer array
target - Each value represents the desired final height at that position
The output is:
- A single integer representing the minimum number of increment operations needed
The constraints are important:
target.lengthcan be as large as100000- Each
target[i]can also be as large as100000
These constraints immediately rule out expensive quadratic or cubic algorithms. We need an algorithm that runs in linear time or close to it.
An important observation is that every operation affects a contiguous segment. That means when heights increase from left to right, we need additional operations to create the extra height. However, when heights decrease, we do not need to remove anything because operations only add values. The previously created layers can simply stop extending.
Important edge cases include:
- Arrays with only one element
- Strictly increasing arrays
- Strictly decreasing arrays
- Flat arrays where all values are identical
- Large alternating rises and drops
The problem guarantees:
- Every value is positive
- The answer fits in a 32-bit integer
- The array is non-empty
These guarantees simplify implementation because we never need to handle negative numbers or empty input.
Approaches
Brute Force Approach
A brute-force strategy would try to simulate the actual operations.
One possible method is:
- Find contiguous ranges where values are still below the target
- Increment those ranges
- Repeat until the constructed array matches the target
For example, with [1,2,3]:
- Increment
[0..2] - Increment
[1..2] - Increment
[2..2]
This eventually works, but repeatedly scanning and modifying the array is extremely inefficient.
Another brute-force interpretation is recursively splitting intervals and trying to construct layers manually. This resembles painting problems where we repeatedly identify minimum heights and subtract layers.
While these approaches are logically correct, they become far too slow for arrays of size 100000.
Key Insight
The critical observation is that we only need new operations when the height increases compared to the previous position.
Suppose we process the array from left to right.
- If
target[i] <= target[i-1], no extra operations are needed at positioni - If
target[i] > target[i-1], we must starttarget[i] - target[i-1]new operations
Why?
Because operations can extend forward across contiguous regions. Any height already built at the previous index can continue into the current index for free. Only the additional height requires new operations.
This transforms the problem into a very simple greedy formula:
$$\text{answer} = target[0] + \sum_{i=1}^{n-1} \max(0, target[i] - target[i-1])$$
This works in linear time and constant extra space.
Although the problem lists monotonic stack as a topic, the greedy observation alone is sufficient and optimal.
Approaches Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n × max(target)) or worse | O(n) | Simulates operations layer by layer |
| Optimal Greedy | O(n) | O(1) | Count only positive height increases |
Algorithm Walkthrough
- Initialize the answer with
target[0].
The first element starts from zero, so we need exactly target[0] operations to build its height.
2. Traverse the array from index 1 to n - 1.
At each position, compare the current value with the previous value.
3. If target[i] > target[i - 1], add the difference to the answer.
This difference represents new layers that could not have been extended from the left side.
4. If target[i] <= target[i - 1], do nothing.
Existing operations from earlier positions can simply stop at the current point or continue through it. 5. Return the accumulated answer.
Why it works
Every increment operation creates one horizontal layer across some contiguous interval.
When moving from left to right:
- Any previously created layer can continue into the next position
- Therefore, previously existing height does not require new operations
- Only additional height above the previous position requires starting new intervals
By summing all positive increases, we count exactly the minimum number of new operations needed.
This greedy strategy is optimal because every extra unit of height increase must come from a newly started operation.
Python Solution
from typing import List
class Solution:
def minNumberOperations(self, target: List[int]) -> int:
operations = target[0]
for i in range(1, len(target)):
if target[i] > target[i - 1]:
operations += target[i] - target[i - 1]
return operations
The implementation directly follows the greedy observation.
We initialize operations with the first element because the array starts from all zeros. Building the first column requires exactly that many operations.
Next, we iterate through the remaining elements.
Whenever the current height exceeds the previous height, we add the difference to the answer. This represents the number of newly started increment operations needed to achieve the additional height.
If the current height is smaller or equal, no new operations are required because existing layers can terminate naturally.
The algorithm uses only a single pass through the array and constant extra memory.
Go Solution
func minNumberOperations(target []int) int {
operations := target[0]
for i := 1; i < len(target); i++ {
if target[i] > target[i-1] {
operations += target[i] - target[i-1]
}
}
return operations
}
The Go implementation mirrors the Python logic closely.
Go slices already provide efficient indexed access, so no additional data structures are needed.
Since the problem guarantees the answer fits within a 32-bit integer, using Go's default int type is sufficient.
Unlike Python, Go does not require explicit type imports or annotations for slices beyond the function signature.
Worked Examples
Example 1
Input:
target = [1,2,3,2,1]
Initial answer:
operations = 1
Traversal:
| Index | Current | Previous | Increase | Operations |
|---|---|---|---|---|
| 1 | 2 | 1 | 1 | 2 |
| 2 | 3 | 2 | 1 | 3 |
| 3 | 2 | 3 | 0 | 3 |
| 4 | 1 | 2 | 0 | 3 |
Final answer:
3
Example 2
Input:
target = [3,1,1,2]
Initial answer:
operations = 3
Traversal:
| Index | Current | Previous | Increase | Operations |
|---|---|---|---|---|
| 1 | 1 | 3 | 0 | 3 |
| 2 | 1 | 1 | 0 | 3 |
| 3 | 2 | 1 | 1 | 4 |
Final answer:
4
Example 3
Input:
target = [3,1,5,4,2]
Initial answer:
operations = 3
Traversal:
| Index | Current | Previous | Increase | Operations |
|---|---|---|---|---|
| 1 | 1 | 3 | 0 | 3 |
| 2 | 5 | 1 | 4 | 7 |
| 3 | 4 | 5 | 0 | 7 |
| 4 | 2 | 4 | 0 | 7 |
Final answer:
7
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through the array |
| Space | O(1) | Only a few integer variables are used |
The algorithm processes each element exactly once, making the runtime linear in the size of the input.
No auxiliary arrays, stacks, or recursion are used, so the extra memory usage remains constant regardless of input size.
Test Cases
from typing import List
class Solution:
def minNumberOperations(self, target: List[int]) -> int:
operations = target[0]
for i in range(1, len(target)):
if target[i] > target[i - 1]:
operations += target[i] - target[i - 1]
return operations
sol = Solution()
assert sol.minNumberOperations([1,2,3,2,1]) == 3 # example 1
assert sol.minNumberOperations([3,1,1,2]) == 4 # example 2
assert sol.minNumberOperations([3,1,5,4,2]) == 7 # example 3
assert sol.minNumberOperations([1]) == 1 # single element
assert sol.minNumberOperations([5]) == 5 # single large element
assert sol.minNumberOperations([1,1,1,1]) == 1 # flat array
assert sol.minNumberOperations([1,2,3,4,5]) == 5 # strictly increasing
assert sol.minNumberOperations([5,4,3,2,1]) == 5 # strictly decreasing
assert sol.minNumberOperations([2,2,2,3,3]) == 3 # plateau then increase
assert sol.minNumberOperations([3,3,1,1,4]) == 6 # drop then rise
assert sol.minNumberOperations([1,3,2,5,4,6]) == 8 # alternating rises
assert sol.minNumberOperations([100000]) == 100000 # max value single element
assert sol.minNumberOperations([100000,100000]) == 100000 # large flat values
Test Case Summary
| Test | Why |
|---|---|
[1,2,3,2,1] |
Validates standard mountain shape |
[3,1,1,2] |
Tests decreasing then increasing |
[3,1,5,4,2] |
Tests large upward jump |
[1] |
Smallest valid array |
[5] |
Single large value |
[1,1,1,1] |
Flat plateau case |
[1,2,3,4,5] |
Strictly increasing sequence |
[5,4,3,2,1] |
Strictly decreasing sequence |
[2,2,2,3,3] |
Plateau followed by increase |
[3,3,1,1,4] |
Multiple plateaus and jumps |
[1,3,2,5,4,6] |
Alternating increases and decreases |
[100000] |
Maximum value edge case |
[100000,100000] |
Large constant heights |
Edge Cases
One important edge case is a strictly increasing array such as [1,2,3,4,5]. A naive implementation might incorrectly attempt to reuse previous operations too aggressively. In reality, every increase requires additional operations. The implementation handles this by adding every positive difference between adjacent elements.
Another important case is a strictly decreasing array like [5,4,3,2,1]. It may seem that additional operations are needed at every index, but all required layers already started earlier and can simply terminate gradually. The algorithm correctly avoids adding anything when the current value is smaller than the previous value.
A third critical case is large flat plateaus such as [7,7,7,7]. An incorrect implementation might repeatedly count the same height multiple times. The greedy solution handles this correctly because equal adjacent values produce a difference of zero, so no extra operations are added.
A final subtle case involves alternating peaks and valleys such as [1,5,2,6,3]. These patterns test whether the algorithm correctly distinguishes between reusable layers and genuinely new layers. By considering only positive increases, the implementation counts exactly the necessary new operations while reusing previous work whenever possible.