题目描述
思路解析
一句话答案:LeetCode 1899 合并三元组以形成目标三元组:逐位取最大只会把数往大抬、绝不调小,所以只留三位都不超 target 的三元组、逐位攒进 best,看能否正好顶成 target,一趟扫描 O(n)、空间 O(1)。
这道题到底在问能不能拼出 target
给一个三元组数组 triplets 和目标三元组 target,长度都是 3。每次挑两个三元组,把其中一个换成两者在每一位上各取较大值的结果,问反复操作能否让某个三元组恰好变成 target。题面给的 target = [5,5,5],triplets = [[2,5,3],[1,8,4],[1,3,5],[4,1,5],[5,2,3]],答案是 true;拼得出返回 true,拼不出返回 false。
模拟两两合并这条路走得通吗
看到‘每次合并两个’,容易想着照操作一步步模拟:先合哪两个、再合哪两个,把配对顺序试一遍。可顺序有指数种,铺开会直接爆掉。而逐位取最大满足交换律和结合律——先合谁后合谁、合几次,最后落在某一位上的值,都等于参与进来的三元组在这一位上的最大值。顺序既然无所谓,就不必真去演具体配对。
哪些三元组能用,哪些碰都不能碰
逐位取最大只会把数往大抬、绝不调小——某一位一旦被顶上去,后面再合别的也压不回来。麻烦就出在‘超标’:一个三元组只要有某一位 > target 对应那位,并进来后那一位就会冲过目标且再降不回去,结果必然对不上。所以能安全参与的,只有三位都 ≤ target 的三元组,任意一位越界就直接跳过。剩下的安全三元组逐位取最大,攒进一个 best,也就是从 [0,0,0] 起、逐位记着眼下最大值的三元组。这就是这道题的贪心:不纠结拿哪两个去配,只认这一位能不能安全抬到位。
写出来就三步:过滤、取最大、判相等
落到代码上就三件事:遍历每个三元组,逐位和 target 比,某一位 > target 就跳过;对留下的安全三元组,把 best 的每一位更新成它和这个三元组在该位上的较大值;扫完判断 best 是不是恰好等于 target。最容易松手的是最后这步——超标的都滤掉了,best 每一位都 ≤ target,所以不能用‘每一位都 ≥ target’这种覆盖式判断,那样没攒够的也会算成功;要的是逐位相等,best = target 才返回 true。
[5,5,5] 这个目标,best 怎么顶上去
target = [5,5,5],best 从 [0,0,0] 起。[2,5,3] 三位都不超 5,安全,best = [2,5,3]。[1,8,4] 中间那位 8 > 5 越界,跳过,best 不动。[1,3,5] 安全,末位被 5 顶起,best = [2,5,5]。[4,1,5] 安全,首位被 4 抬高,best = [4,5,5]。[5,2,3] 安全,首位被 5 顶满,best = [5,5,5]。扫完 best 和 target 一位不差,返回 true。
两个地方一手滑,答案就反了
第一个坑是没过滤就把所有三元组一股脑逐位取最大——混进一个某位超标的,那位当场冲破 target 且降不回来,结果直接错。第二个坑是最后拿‘best 每位都 ≥ target’判成功,可 best 不可能超过 target,没攒够的那位其实小于 target,覆盖式判断会放它过去,必须判 best 和 target 每位相等。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记住两件事:有一位超标的三元组直接跳过;其余的全逐位取最大攒进 best。最后 best == target 就成。
目标是 [5,5,5]。我们要攒一个 best,从 [0,0,0] 开始,把每个安全的三元组逐位取最大并进来。
轮到第 1 个三元组 [2,5,3]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [2,5,3] 逐位取最大,第 1、2、3 位被抬高,best 现在是 [2,5,3]。
轮到第 2 个三元组 [1,8,4]。先逐位和目标 [5,5,5] 比:有位置超过了目标([1,8,4] vs [5,5,5]),它被污染了,跳过。
跳过这个三元组。标红的是它超标的位置——一旦掺进来这些位就会顶破目标且降不回去。best 保持 [2,5,3] 不动。
轮到第 3 个三元组 [3,2,1]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [3,2,1] 逐位取最大,第 1 位被抬高,best 现在是 [3,5,3]。
轮到第 4 个三元组 [1,3,5]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [1,3,5] 逐位取最大,第 3 位被抬高,best 现在是 [3,5,5]。
轮到第 5 个三元组 [6,1,2]。先逐位和目标 [5,5,5] 比:有位置超过了目标([6,1,2] vs [5,5,5]),它被污染了,跳过。
跳过这个三元组。标红的是它超标的位置——一旦掺进来这些位就会顶破目标且降不回去。best 保持 [3,5,5] 不动。
轮到第 6 个三元组 [4,1,5]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [4,1,5] 逐位取最大,第 1 位被抬高,best 现在是 [4,5,5]。
轮到第 7 个三元组 [2,2,2]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [2,2,2] 逐位取最大,best 现在是 [4,5,5]。
轮到第 8 个三元组 [1,9,1]。先逐位和目标 [5,5,5] 比:有位置超过了目标([1,9,1] vs [5,5,5]),它被污染了,跳过。
跳过这个三元组。标红的是它超标的位置——一旦掺进来这些位就会顶破目标且降不回去。best 保持 [4,5,5] 不动。
轮到第 9 个三元组 [5,2,3]。先逐位和目标 [5,5,5] 比:每一位都不超过目标,可以安全合并。
安全,合并进来:best 和 [5,2,3] 逐位取最大,第 1 位被抬高,best 现在是 [5,5,5]。
所有安全三元组都攒完了。best = [5,5,5],正好等于目标 [5,5,5],所以答案是 true。
三个高频追问:为何不用模拟、什么是可用三元组、为何判相等。
参考代码
def mergeTriplets(triplets, target): best = [0, 0, 0] # 逐位攒到的最大 for t in triplets: if all(t[j] <= target[j] # 每一位都不超目标 for j in range(3)): # 才算安全三元组 for j in range(3): best[j] = max(best[j], t[j]) # 逐位取最大 return best == target # 攒完是否正好等于目标复杂度
- 时间:O(n),每个三元组只看一次,三个位置是常数次比较
- 空间:O(1),只用一个长度 3 的 best,不随输入增长
易错点
面试追问把动画讲成自己的话
追问为什么不用真的去模拟两两合并的过程?
追问哪些三元组算「可用」?
追问最后为什么是判相等而不是判 best 覆盖 target?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
划分字母区间
LeetCode 763 · 中等 · 沿着 贪心 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题