197 lines
10 KiB
YAML
197 lines
10 KiB
YAML
title: Merge Triplets to Form Target Triplet
|
|
slug: merge-triplets-to-form-target-triplet
|
|
difficulty: medium
|
|
leetcode_id: 1899
|
|
leetcode_url: https://leetcode.com/problems/merge-triplets-to-form-target-triplet/
|
|
categories:
|
|
- arrays
|
|
patterns:
|
|
- greedy
|
|
|
|
description: |
|
|
A **triplet** is an array of three integers. You are given a 2D integer array `triplets`, where `triplets[i] = [a_i, b_i, c_i]` describes the i<sup>th</sup> **triplet**. You are also given an integer array `target = [x, y, z]` that describes the **triplet** you want to obtain.
|
|
|
|
To obtain `target`, you may apply the following operation on `triplets` **any number** of times (possibly **zero**):
|
|
|
|
- Choose two indices (0-indexed) `i` and `j` (`i != j`) and **update** `triplets[j]` to become `[max(a_i, a_j), max(b_i, b_j), max(c_i, c_j)]`.
|
|
- For example, if `triplets[i] = [2, 5, 3]` and `triplets[j] = [1, 7, 5]`, `triplets[j]` will be updated to `[max(2, 1), max(5, 7), max(3, 5)] = [2, 7, 5]`.
|
|
|
|
Return `true` *if it is possible to obtain the* `target` *triplet* `[x, y, z]` *as an element of* `triplets`, *or* `false` *otherwise*.
|
|
|
|
constraints: |
|
|
- `1 <= triplets.length <= 10^5`
|
|
- `triplets[i].length == target.length == 3`
|
|
- `1 <= a_i, b_i, c_i, x, y, z <= 1000`
|
|
|
|
examples:
|
|
- input: "triplets = [[2,5,3],[1,8,4],[1,7,5]], target = [2,7,5]"
|
|
output: "true"
|
|
explanation: "Choose the first and last triplets. Update the last to [max(2,1), max(5,7), max(3,5)] = [2,7,5]. The target triplet is now an element of triplets."
|
|
- input: "triplets = [[3,4,5],[4,5,6]], target = [3,2,5]"
|
|
output: "false"
|
|
explanation: "It is impossible to have [3,2,5] as an element because there is no 2 in any of the triplets."
|
|
- input: "triplets = [[2,5,3],[2,3,4],[1,2,5],[5,2,3]], target = [5,5,5]"
|
|
output: "true"
|
|
explanation: "Through multiple merge operations, we can form [5,5,5] by combining values from different triplets."
|
|
|
|
explanation:
|
|
intuition: |
|
|
Imagine you have a collection of puzzle pieces, where each piece has three slots that can only increase in value when combined with another piece. Your goal is to assemble the exact target combination.
|
|
|
|
The key insight is that the `max` operation is **monotonically increasing** — once a value in a position exceeds the target, it can never decrease. This means if any triplet has *any* value greater than the corresponding target value, that triplet is **poisonous** and cannot be used at all.
|
|
|
|
Think of it like this: you're collecting "good" triplets that don't exceed the target in any position. From these safe triplets, you need to find ones that can contribute each target value. If triplet A has the correct first value, triplet B has the correct second value, and triplet C has the correct third value — you can merge them all together (in any order) to get the target.
|
|
|
|
The order of merging doesn't matter because `max` is both **commutative** and **associative**. You just need to verify that *somewhere* among the valid triplets, each target position's exact value exists.
|
|
|
|
approach: |
|
|
We solve this using a **Greedy Filter-and-Check** approach:
|
|
|
|
**Step 1: Initialise tracking variables**
|
|
|
|
- `found`: A set or three boolean flags to track which target positions we've matched
|
|
- We need to find triplets that can contribute `target[0]`, `target[1]`, and `target[2]`
|
|
|
|
|
|
|
|
**Step 2: Filter and check each triplet**
|
|
|
|
- For each triplet `[a, b, c]`, first check if it's **valid** (not poisonous)
|
|
- A triplet is valid only if `a <= target[0]` AND `b <= target[1]` AND `c <= target[2]`
|
|
- If any value exceeds its target counterpart, skip this triplet entirely
|
|
- For valid triplets, check which positions exactly match the target and mark them as found
|
|
|
|
|
|
|
|
**Step 3: Return the result**
|
|
|
|
- Return `true` if all three target positions have been matched by at least one valid triplet
|
|
- Return `false` otherwise
|
|
|
|
|
|
|
|
This works because we can always merge all valid triplets together. If each target value appears in at least one valid triplet, the final merged result will contain exactly those values (since `max` of equal values is the value itself, and we filtered out anything larger).
|
|
|
|
common_pitfalls:
|
|
- title: Using Invalid Triplets
|
|
description: |
|
|
A critical mistake is trying to use a triplet that exceeds the target in any position.
|
|
|
|
For example, with `target = [2, 7, 5]` and a triplet `[1, 8, 4]`, you might think "I can use this for the third position since 4 <= 5." But the second position has 8 > 7, so merging this triplet would **permanently corrupt** the result — you'd end up with at least 8 in position 2, which exceeds the target.
|
|
|
|
Always check *all three* positions before considering a triplet valid.
|
|
wrong_approach: "Check positions independently without validating the whole triplet"
|
|
correct_approach: "Filter out triplets where ANY position exceeds the target"
|
|
|
|
- title: Simulating the Merge Process
|
|
description: |
|
|
Some approaches try to actually simulate merging triplets step by step, tracking the current state. This is unnecessary and complicates the solution.
|
|
|
|
Since `max` is commutative and associative, the order of operations doesn't matter. You don't need to track intermediate states — just verify that the required values *exist* among valid triplets.
|
|
wrong_approach: "Simulate merging operations and track state"
|
|
correct_approach: "Simply check if each target value exists in at least one valid triplet"
|
|
|
|
- title: Missing Values in Triplets
|
|
description: |
|
|
If the target requires a value that doesn't appear in any valid triplet, it's impossible to construct.
|
|
|
|
For example, with `target = [3, 2, 5]` and triplets `[[3,4,5],[4,5,6]]`, the value `2` doesn't exist anywhere. Even though triplet `[3,4,5]` has `3` and `5`, neither triplet can contribute `2`. Since `max` can only maintain or increase values, you cannot create a value smaller than anything present.
|
|
|
|
key_takeaways:
|
|
- "**Greedy filtering**: When operations are monotonic (like `max`), filter out elements that would make the goal impossible before processing"
|
|
- "**Decomposition insight**: Complex operations can sometimes be reduced to simpler existence checks — here, we just need to verify each target component exists in some valid triplet"
|
|
- "**Commutative operations**: When operation order doesn't matter, you don't need to simulate the process — just verify the preconditions"
|
|
- "**Foundation for similar problems**: This pattern of filtering invalid candidates and checking coverage applies to many greedy problems"
|
|
|
|
time_complexity: "O(n). We iterate through the triplets array once, performing constant-time checks for each triplet."
|
|
space_complexity: "O(1). We only use a fixed number of boolean variables to track which target positions have been matched."
|
|
|
|
solutions:
|
|
- approach_name: Greedy Filter-and-Check
|
|
is_optimal: true
|
|
code: |
|
|
def merge_triplets(triplets: list[list[int]], target: list[int]) -> bool:
|
|
# Track which target positions we've matched
|
|
found = [False, False, False]
|
|
|
|
for triplet in triplets:
|
|
# Skip triplets that exceed target in any position (poisonous)
|
|
if (triplet[0] > target[0] or
|
|
triplet[1] > target[1] or
|
|
triplet[2] > target[2]):
|
|
continue
|
|
|
|
# This triplet is valid - check which positions match exactly
|
|
for i in range(3):
|
|
if triplet[i] == target[i]:
|
|
found[i] = True
|
|
|
|
# Return true if all three positions have been matched
|
|
return all(found)
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass through the triplets array.
|
|
|
|
**Space Complexity:** O(1) — Only three boolean flags used.
|
|
|
|
We filter out invalid triplets (those exceeding target in any position) and track which target values we've found among the valid ones. If all three positions can be matched, we can merge the contributing triplets to form the target.
|
|
|
|
- approach_name: Set-Based Tracking
|
|
is_optimal: true
|
|
code: |
|
|
def merge_triplets(triplets: list[list[int]], target: list[int]) -> bool:
|
|
# Use a set to track which positions we can match
|
|
matched = set()
|
|
|
|
for a, b, c in triplets:
|
|
# Skip if any value exceeds the target
|
|
if a > target[0] or b > target[1] or c > target[2]:
|
|
continue
|
|
|
|
# Add indices where this triplet matches the target
|
|
if a == target[0]:
|
|
matched.add(0)
|
|
if b == target[1]:
|
|
matched.add(1)
|
|
if c == target[2]:
|
|
matched.add(2)
|
|
|
|
# Early exit if we've found all three
|
|
if len(matched) == 3:
|
|
return True
|
|
|
|
return len(matched) == 3
|
|
explanation: |
|
|
**Time Complexity:** O(n) — Single pass, with early exit optimisation.
|
|
|
|
**Space Complexity:** O(1) — Set contains at most 3 elements.
|
|
|
|
This variant uses a set for cleaner tracking and includes an early exit when all three positions are matched. The early exit can provide a speedup when valid triplets appear early in the array.
|
|
|
|
- approach_name: Brute Force Simulation
|
|
is_optimal: false
|
|
code: |
|
|
def merge_triplets(triplets: list[list[int]], target: list[int]) -> bool:
|
|
n = len(triplets)
|
|
|
|
# Try all possible subsets of triplets to merge
|
|
for mask in range(1, 1 << n):
|
|
# Start with zeros and merge selected triplets
|
|
result = [0, 0, 0]
|
|
|
|
for i in range(n):
|
|
if mask & (1 << i):
|
|
result[0] = max(result[0], triplets[i][0])
|
|
result[1] = max(result[1], triplets[i][1])
|
|
result[2] = max(result[2], triplets[i][2])
|
|
|
|
if result == target:
|
|
return True
|
|
|
|
return False
|
|
explanation: |
|
|
**Time Complexity:** O(2^n * n) — Exponential in the number of triplets.
|
|
|
|
**Space Complexity:** O(1) — Only storing the current merged result.
|
|
|
|
This brute force approach tries all possible subsets of triplets to merge. While correct, it's far too slow for the constraint `n <= 10^5` — it would require up to 2^100000 iterations! Included here to illustrate why the greedy insight is essential.
|