Card 05/ 08
Two Pointers Example: Pairing Riders on Tandem Bikes
Each tandem carries two riders under a weight limit: sort the riders, then pair the heaviest left with the lightest whenever they fit.
Pairing items under a limit to use as few containers as possible is an opposite-ends pair search, even when the problem never mentions a sum. A tour hires out tandem bikes, each carrying at most two riders with a combined weight of 150 kilograms. Five riders weigh 60, 95, 70, 80 and 55 kilograms. The question is the fewest bikes that carry everyone.
The cue is hidden in one observation: the heaviest rider either shares with the lightest rider or rides alone. If the heaviest cannot fit with the lightest, they fit with nobody. If they can, pairing them is never worse than any other choice, because any rider who could have shared with the lightest is no heavier than the heaviest and can still share with someone. Sorting the riders puts the lightest and the heaviest at the two ends, which is the two pointers' starting position.
Each step settles the heaviest rider still waiting. If the riders at the two pointers fit together, both board one bike and both pointers move in; otherwise the heaviest rides alone and only the right pointer moves. When the pointers meet on one rider, that rider takes a bike alone.
Inputriders 60 95 70 80 55 kilograms, sorted to 55 60 70 80 95, limit 150
Bike 1
- 55left
- 60
- 70
- 80
- 95right
55 + 95 = 150, fits: 55 and 95 share a bike
Bike 2
- 55
- 60left
- 70
- 80right
- 95
60 + 80 = 140, fits: 60 and 80 share a bike
Bike 3
- 55
- 60
- 70leftright
- 80
- 95
one rider left: 70 rides alone
ResultThree bikes: 55 with 95, 60 with 80, and 70 alone.
The listing runs the pairing on the five riders, on two riders too heavy to share, and on nobody.
def tandem_bikes(weights: list[int], limit: int) -> list[tuple[int, ...]]:
riders = sorted(weights)
left, right = 0, len(riders) - 1
bikes: list[tuple[int, ...]] = []
while left <= right:
if left < right and riders[left] + riders[right] <= limit:
bikes.append((riders[left], riders[right]))
left += 1
else:
bikes.append((riders[right],))
right -= 1
return bikes
for weights in ([60, 95, 70, 80, 55], [140, 145], []):
bikes = tandem_bikes(weights, 150)
print(f"{weights} limit 150: {len(bikes)} bikes {bikes}")[60, 95, 70, 80, 55] limit 150: 3 bikes [(55, 95), (60, 80), (70,)]
[140, 145] limit 150: 2 bikes [(145,), (140,)]
[] limit 150: 0 bikes []The loop runs while left <= right, unlike the pair search, because a single rider left between the pointers still needs a bike. The cost is O(n log n) for the sort and O(n) for the pass, for n riders. The same reasoning holds for any items paired two at a time under a limit, such as boats or delivery crates. A TypeScript version must sort with (a, b) => a - b: the default sort compares numbers as strings and would place 100 before 55.
← → move through sections and cards