The problem
You get a list of triplets [a, b, c] and a target triplet. One operation picks two triplets and replaces one of them with their element-wise maximum. Return True if, after any number of operations, target can appear in the list.
Input: triplets = [[2, 5, 3], [1, 8, 4], [1, 7, 5]], target = [2, 7, 5]
Output: TrueMerge [2, 5, 3] and [1, 7, 5]: the max is [2, 7, 5], which is the target. [1, 8, 4] is never used.
How to think about it
Start with brute force. The result of any sequence of merges is just the element-wise max of some subset of triplets. So you could try all 2ⁿ subsets and see if any one’s max equals target. That is exponential.
First observation: max only goes up. Once a merge includes a triplet with any value above the target in some position, that position can never come back down. So any triplet with t[i] > target[i] for some i is poison: it can never be part of the answer. Throw it away.
The key insight: merging every safe triplet never hurts. A safe triplet has every value at or below the target. Merging it in can’t push any position past the target. So the best you can do is merge all safe triplets together and check whether the result equals target. That happens exactly when, for each of the three positions, some safe triplet hits the target value exactly there. Track the set of positions that have been matched; succeed if all three are covered.
Which pattern is this? Greedy filtering. The tell is a monotone operation (max, OR, union): anything that overshoots is useless, and everything else is free to include.
Approach
- Keep a set
goodof positions (0, 1, 2) that some safe triplet matches exactly. - For each triplet
t, check it is safe:t[i] <= target[i]for all three positions. - If it is safe, add each position
iwheret[i] == target[i]togood. - Unsafe triplets are skipped entirely.
- Return
Trueifgoodhas all three positions.
Solution
def merge_triplets(triplets, target):
good = set() # positions matched exactly so far
for t in triplets:
if all(t[i] <= target[i] for i in range(3)): # safe: never overshoots the target
for i in range(3):
if t[i] == target[i]: good.add(i)
return len(good) == 3
# merge_triplets([[2,5,3],[1,8,4],[1,7,5]], [2,7,5]) -> True
# merge_triplets([[3,4,5],[4,5,6]], [3,2,5]) -> FalseDry run
With target = [2, 7, 5]:
| Triplet | Safe? | Positions matched | good after |
|---|---|---|---|
| [2, 5, 3] | yes | 0 | {0} |
| [1, 8, 4] | no, 8 > 7 | — | {0} |
| [1, 7, 5] | yes | 1, 2 | {0, 1, 2} |
All three positions are covered, so True. In the second example, [3, 4, 5] has 4 > 2 and [4, 5, 6] overshoots everywhere, so nothing is safe and the answer is False.
Complexity
- Time: O(n). Each triplet is checked in constant time.
- Space: O(1). The set holds at most three positions.
Edge cases and pitfalls
- A triplet equal to the target answers
Trueby itself. - No safe triplets means
goodstays empty:False. - Matching a position with an unsafe triplet is the main bug.
[2, 9, 0]matches position 0 of[2, 7, 5], but its 9 ruins position 1, so it must not count. - Trying to choose a clever subset is unnecessary; including every safe triplet is always at least as good.
Related problems
- Hand of Straights (LeetCode 846): greedy where each decision is forced, so no search is needed.
- Partition Labels (LeetCode 763): another one-pass greedy over a monotone quantity (the furthest end).
- Gas Station (LeetCode 134): a feasibility check followed by a simple linear scan.
- 1.Assign Cookies (LeetCode 455)
- 2.Jump Game (LeetCode 55)
- 3.Jump Game II (LeetCode 45)
- 4.Gas Station (LeetCode 134)
- 5.Partition Labels (LeetCode 763)
- 6.Task Scheduler (LeetCode 621)
- 7.Hand of Straights (LeetCode 846)
- 8.Remove Covered Intervals (LeetCode 1288)
- 9.Valid Parenthesis String (LeetCode 678)
- 10.Merge Triplets to Form Target Triplet (LeetCode 1899)
- 11.Candy (LeetCode 135)