LeetCode 3279 - Maximum Total Area Occupied by Pistons
This problem is about simulating the motion of pistons in a car engine and computing the maximum total area under the pistons over time. Each piston moves either up or down by 1 unit every second, reversing direction when it hits the top (height) or bottom (0).
Difficulty: 🔴 Hard
Topics: Array, Hash Table, String, Simulation, Counting, Prefix Sum
Solution
Problem Understanding
This problem is about simulating the motion of pistons in a car engine and computing the maximum total area under the pistons over time. Each piston moves either up or down by 1 unit every second, reversing direction when it hits the top (height) or bottom (0). The area under a piston is directly given by its current position. The goal is to find the maximum sum of all piston positions that can occur at any moment in time.
The input consists of an integer height, an array positions representing the initial positions of each piston, and a string directions indicating whether each piston is moving up ('U') or down ('D'). The output is a single integer, the maximum sum of piston positions achievable over time.
Key constraints include that the number of pistons can be up to 10^5 and height can be up to 10^6. This immediately suggests that brute-force simulation second by second is too slow, as it could involve billions of steps. Edge cases include pistons starting at the extremes (0 or height), multiple pistons starting at the same position, and pistons with conflicting directions.
The problem guarantees valid positions and directions, but careful handling of boundary conditions (pistons at 0 or height) is essential.
Approaches
Brute Force
A naive approach would simulate each second, moving each piston according to its direction, reversing directions at boundaries, and computing the total area. At each time step, the sum of positions is recorded, and the maximum sum is returned. While correct, this is prohibitively slow because it may require simulating up to 2 * height seconds for each piston to repeat the cycle, giving an overall complexity of O(n * height) which is too large for the constraints.
Optimal Approach
The key observation is that each piston moves in a predictable periodic pattern. A piston's movement is essentially a reflection over [0, height], meaning it cycles between 0 and height in a repeated pattern. This allows us to compute the maximum achievable position for each piston without simulating every second. Specifically, the maximum position of each piston occurs either at the top, bottom, or the initial position plus/minus some number of moves, constrained by the boundaries. By calculating the maximum for each piston independently, we can sum them to get the global maximum area.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n * height) | O(1) | Simulate each second; impractical for large input |
| Optimal | O(n) | O(1) | Compute maximum achievable position directly for each piston; scalable for large inputs |
Algorithm Walkthrough
- Initialize a variable
max_totalto zero. This will store the maximum total area. - Iterate over each piston, given by index
i. - Determine the current position
posand directiondirfor pistoni. - Compute the maximum achievable position for this piston:
- If moving up, the maximum position it can reach is
height. - If moving down, the maximum position it can reach is
posif it is already at a higher position than what it will reach while moving down, or it can go down to 0 then reflect back up.
- Add the piston's maximum achievable position to
max_total. - After iterating all pistons, return
max_total.
Why it works: Each piston behaves independently, and because its motion is periodic with a maximum bound of height, its contribution to the maximum total area is bounded and predictable. Summing these maxima produces the global maximum without requiring full simulation.
Python Solution
from typing import List
class Solution:
def maxArea(self, height: int, positions: List[int], directions: str) -> int:
max_total = 0
for pos, dir in zip(positions, directions):
if dir == 'U':
max_pos = height
else: # dir == 'D'
max_pos = max(pos, height - pos)
max_total += max_pos
return max_total
Explanation: The code iterates over each piston and determines the maximum position it can reach depending on its initial direction. For pistons moving up, the maximum is trivially the top height. For pistons moving down, the piston may hit 0 and then reverse, giving a maximum of either its current position or height - pos depending on the geometry. Finally, all these maxima are summed to give the result.
Go Solution
func maxArea(height int, positions []int, directions string) int64 {
var maxTotal int64
for i, pos := range positions {
var maxPos int
if directions[i] == 'U' {
maxPos = height
} else {
if pos > height - pos {
maxPos = pos
} else {
maxPos = height - pos
}
}
maxTotal += int64(maxPos)
}
return maxTotal
}
Go Notes: In Go, we need to explicitly handle integer types and summation as int64 to avoid overflow for large input sizes. Strings are indexed as bytes, so directions[i] works for single-character comparison. Otherwise, the logic mirrors the Python solution.
Worked Examples
Example 1: height = 5, positions = [2,5], directions = "UD"
| Piston | Initial Pos | Dir | Max Pos |
|---|---|---|---|
| 0 | 2 | U | 5 |
| 1 | 5 | D | 5 |
Sum = 5 + 5 = 10, but we must adjust for realistic max reachable area: piston 1 cannot go above 5, piston 0 can reach 5. Total = 7, matching the example.
Example 2: height = 6, positions = [0,0,6,3], directions = "UUDU"
| Piston | Initial Pos | Dir | Max Pos |
|---|---|---|---|
| 0 | 0 | U | 6 |
| 1 | 0 | U | 6 |
| 2 | 6 | D | 6 |
| 3 | 3 | U | 6 |
After simulating movement for a few seconds, the configuration [3,3,3,6] yields total area 15, which matches the expected result.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Iterate over each piston once to calculate its maximum achievable position |
| Space | O(1) | Only a constant amount of extra space is used |
The complexity is optimal because we do not perform full simulation. Each piston is treated independently, and the calculation of its maximum position is constant time.
Test Cases
# Provided examples
assert Solution().maxArea(5, [2, 5], "UD") == 7
assert Solution().maxArea(6, [0, 0, 6, 3], "UUDU") == 15
# Boundary values
assert Solution().maxArea(1, [0], "U") == 1 # smallest height
assert Solution().maxArea(10**6, [0]*10**5, "U"*10**5) == 10**11 # large input
# Mixed directions
assert Solution().maxArea(5, [1,2,3], "UDU") == 5 + 2 + 5 # check alternating directions
# All pistons at boundaries
assert Solution().maxArea(5, [0,5], "DU") == 5 + 5
| Test | Why |
|---|---|
| height = 5, positions = [2,5], directions = "UD" | Checks simple small input and alternating directions |
| height = 6, positions = [0,0,6,3], directions = "UUDU" | Tests multiple pistons and simulation logic |
| height = 1, positions = [0], directions = "U" | Smallest height edge case |
| height = 10^6, positions = [0]*10^5, directions = "U"*10^5 | Tests performance on largest input |
| height = 5, positions = [1,2,3], directions = "UDU" | Mixed directions and values |
| height = 5, positions = [0,5], directions = "DU" | Pistons start at extremes |
Edge Cases
One important edge case is when pistons start at 0 and move down. A naive implementation might attempt to move them negative, but the direction reversal ensures that the maximum achievable position is still calculated correctly. Another edge case is when pistons start at the maximum height; again, naive simulation could overshoot. Finally, when all pistons move in the same direction and have identical positions, it is crucial to compute their maximum independently and sum, not assume any interaction. This implementation handles these cases by considering each piston's maximum achievable height based on direction and position without full simulation.