LeetCode 3502 - Minimum Cost to Reach Every Position
We are given an array cost of length n. There are n + 1 positions in a line, numbered from 0 to n. Initially, we are standing at position n, which means we are at the very end of the line. To move forward in the line, we can swap positions with other people.
Difficulty: 🟢 Easy
Topics: Array
Solution
Problem Understanding
We are given an array cost of length n. There are n + 1 positions in a line, numbered from 0 to n. Initially, we are standing at position n, which means we are at the very end of the line.
To move forward in the line, we can swap positions with other people. The cost of swapping depends on whether that person is currently in front of us or behind us:
- If a person is in front of us, we must pay
cost[i]to swap with personi. - If a person is behind us, swapping with them is free.
For every position i from 0 to n - 1, we must determine the minimum total amount of money needed to eventually stand at position i.
A key detail is that after paying to swap with someone in front of us, our position changes. This can make other people move behind us, allowing future swaps with them at no additional cost.
The input consists of:
cost[i], the amount personicharges if we swap with them while they are in front of us.
The output should be an array answer where:
answer[i]is the minimum cost required to reach positioni.
The constraints are very small:
1 <= n <= 1001 <= cost[i] <= 100
Although the constraints allow even quadratic solutions, understanding the underlying observation leads to a very simple linear solution.
Important edge cases include arrays of length one, strictly increasing costs, strictly decreasing costs, and situations where the cheapest person appears somewhere in the middle of the line. These cases help reveal the key property of the problem.
Approaches
Brute Force
For a target position i, we can consider every possible person j with j <= i as the first person we pay.
Suppose we pay person j. After swapping with them, we immediately move to position j. Since position j is at or before position i, every person from j through i is now behind us or at our position. We can reach position i through free swaps.
Therefore, to reach position i, we only need to choose some person among positions 0 through i and pay their cost once.
A brute-force solution would evaluate every prefix [0..i] and find the minimum value inside that prefix by scanning it. Repeating this for every position gives the correct answer.
The drawback is that for each position we scan all previous positions, resulting in quadratic time complexity.
Key Insight
To reach position i, we only need to pay one person in the prefix 0..i.
Why?
If we pay person j where j <= i, we move directly to position j. Since position i is behind or equal to our current position, we can then reach i using only free swaps.
Therefore, the minimum cost to reach position i is simply:
$$\min(cost[0], cost[1], \dots, cost[i])$$
This means the answer array is just the prefix minimum of the input array.
As we scan from left to right, we maintain the smallest cost seen so far and store it as the answer for the current position.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n²) | O(1) excluding output | For each position, scan the entire prefix to find the minimum |
| Optimal | O(n) | O(1) excluding output | Maintain a running prefix minimum |
Algorithm Walkthrough
- Create an answer array of length
n. - Maintain a variable
minimumCostthat stores the smallest cost encountered so far. - Initialize
minimumCostwithcost[0]. - Traverse the array from left to right.
- For each position
i, updateminimumCostas the minimum of its current value andcost[i]. - Store
minimumCostintoanswer[i]. - Continue until all positions have been processed.
- Return the completed answer array.
Why it works
For any target position i, we may choose to pay any person in the prefix 0..i. Paying person j immediately moves us to position j. Since j <= i, position i is no longer in front of us, so we can reach it using free swaps. Thus the optimal strategy is to pay the cheapest person among positions 0 through i. The algorithm maintains exactly this prefix minimum, so every answer is correct.
Python Solution
from typing import List
class Solution:
def minCosts(self, cost: List[int]) -> List[int]:
answer = []
minimum_cost = float("inf")
for value in cost:
minimum_cost = min(minimum_cost, value)
answer.append(minimum_cost)
return answer
The implementation directly follows the prefix minimum observation.
The variable minimum_cost stores the smallest cost seen so far while scanning the array from left to right. For every element, we update this running minimum and append it to the answer array.
Because the minimum value of a prefix can only stay the same or decrease, a single pass is sufficient. Each position receives the minimum value among all costs encountered up to that point, which is exactly the minimum cost required to reach that position.
Go Solution
func minCosts(cost []int) []int {
n := len(cost)
answer := make([]int, n)
minimumCost := cost[0]
for i := 0; i < n; i++ {
if cost[i] < minimumCost {
minimumCost = cost[i]
}
answer[i] = minimumCost
}
return answer
}
The Go implementation mirrors the Python solution. A running prefix minimum is maintained while iterating through the slice. Since all values are at most 100, integer overflow is not a concern. The answer slice is preallocated with length n, allowing direct assignment at each index.
Worked Examples
Example 1
Input
cost = [5,3,4,1,3,2]
Processing:
| i | cost[i] | Running Minimum | answer |
|---|---|---|---|
| 0 | 5 | 5 | [5] |
| 1 | 3 | 3 | [5,3] |
| 2 | 4 | 3 | [5,3,3] |
| 3 | 1 | 1 | [5,3,3,1] |
| 4 | 3 | 1 | [5,3,3,1,1] |
| 5 | 2 | 1 | [5,3,3,1,1,1] |
Final result:
[5,3,3,1,1,1]
For example, position 5 can be reached by paying person 3 a cost of 1, moving to position 3, and then using free swaps to reach position 5.
Example 2
Input
cost = [1,2,4,6,7]
Processing:
| i | cost[i] | Running Minimum | answer |
|---|---|---|---|
| 0 | 1 | 1 | [1] |
| 1 | 2 | 1 | [1,1] |
| 2 | 4 | 1 | [1,1,1] |
| 3 | 6 | 1 | [1,1,1,1] |
| 4 | 7 | 1 | [1,1,1,1,1] |
Final result:
[1,1,1,1,1]
Once we pay person 0 a cost of 1, every other position becomes reachable through free swaps.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through the array |
| Space | O(1) | Only a running minimum is maintained, excluding the output array |
The algorithm processes each element exactly once. Every iteration performs a constant amount of work, consisting of one comparison and one assignment. Aside from the output array required by the problem, only a single variable is used to track the current prefix minimum.
Test Cases
from typing import List
class Solution:
def minCosts(self, cost: List[int]) -> List[int]:
answer = []
minimum_cost = float("inf")
for value in cost:
minimum_cost = min(minimum_cost, value)
answer.append(minimum_cost)
return answer
s = Solution()
assert s.minCosts([5,3,4,1,3,2]) == [5,3,3,1,1,1] # Example 1
assert s.minCosts([1,2,4,6,7]) == [1,1,1,1,1] # Example 2
assert s.minCosts([7]) == [7] # Minimum size array
assert s.minCosts([5,4,3,2,1]) == [5,4,3,2,1] # Strictly decreasing
assert s.minCosts([1,2,3,4,5]) == [1,1,1,1,1] # Strictly increasing
assert s.minCosts([4,4,4,4]) == [4,4,4,4] # All equal values
assert s.minCosts([9,2,8,1,7]) == [9,2,2,1,1] # Multiple minimum updates
assert s.minCosts([10,1,10,10,10]) == [10,1,1,1,1] # Early minimum dominates
assert s.minCosts([3,1,2,1,5]) == [3,1,1,1,1] # Repeated minimum values
Test Summary
| Test | Why |
|---|---|
[5,3,4,1,3,2] |
Validates the first example |
[1,2,4,6,7] |
Validates the second example |
[7] |
Smallest allowed input |
[5,4,3,2,1] |
Prefix minimum changes every step |
[1,2,3,4,5] |
Prefix minimum never changes |
[4,4,4,4] |
Handles duplicate values |
[9,2,8,1,7] |
Multiple decreases in minimum |
[10,1,10,10,10] |
Early minimum dominates all later positions |
[3,1,2,1,5] |
Repeated occurrences of the same minimum |
Edge Cases
Single Element Array
When n = 1, there is only one person in front of us. The answer must simply be [cost[0]]. Implementations that assume multiple elements or incorrectly initialize the running minimum can fail here. The presented solution handles this naturally because the loop processes exactly one value.
Strictly Increasing Costs
Consider cost = [1,2,3,4,5]. The cheapest person appears at the beginning. Once we pay that person, every later position becomes reachable for free. The answer should therefore be [1,1,1,1,1]. This case verifies that the algorithm correctly preserves the smallest prefix value rather than using the current value.
Strictly Decreasing Costs
Consider cost = [5,4,3,2,1]. Every new position introduces a cheaper option than all previous positions. The prefix minimum changes at every step, producing [5,4,3,2,1]. This validates that the running minimum is updated correctly during traversal.
Repeated Minimum Values
Consider cost = [3,1,2,1,5]. The minimum value appears multiple times. The correct answer remains [3,1,1,1,1]. The algorithm works correctly because it only needs to track the minimum value itself, not the position where that minimum first appeared.
Minimum Cost Appearing in the Middle
Consider cost = [8,7,1,9,10]. The cheapest person is neither first nor last. Once position 2 is reached, all subsequent answers should remain 1. This case demonstrates the central insight that the answer for each index is simply the minimum cost within its prefix.