画的颜色混合与线段合并 —— 「最少区间数表示混合颜色」问题解析与实现
题目描述
给定一个细长的画,抽象为数轴上的若干线段,每个线段带有独一无二的颜色编号。线段之间可能重叠,重叠部分的颜色会“混合”,混合颜色用集合表示,集合中元素是所有重叠线段的颜色。
为了简化,集合颜色用集合元素之和来表示。
输入是二维整数数组 segments
,其中每个元素 [start, end, color]
表示半开区间 [start, end)
的线段及其颜色。
任务是用尽量少的不重叠半开区间,表示最终这幅画的颜色混合结果。输出二维数组 painting
,其中每个元素 [left, right, mix]
表示区间 [left, right)
的颜色和 mix
。
具体描述与限制
segments[i] = [starti, endi, colori]
,半开区间[starti, endi)
,颜色colori
唯一。- 重叠区域颜色混合,混合颜色用集合元素和表示。
- 要求最少数目不重叠区间覆盖整个被涂区域。
- 半开区间定义:包含左端点,不包含右端点。
- 输出区间不能出现颜色和为0(表示无颜色)。
- 每种颜色唯一,不会重复。
1 <= segments.length <= 2*10^4
1 <= starti < endi <= 10^5
1 <= colori <= 10^9
示例说明
示例1
输入:
segments = [[1,4,5],[4,7,7],[1,7,9]]
输出:
[[1,4,14],[4,7,16]]
说明:
[1,4)
区间内颜色为{5,9}
,和为14
[4,7)
区间内颜色为{7,9}
,和为16
示例2
输入:
segments = [[1,7,9],[6,8,15],[8,10,7]]
输出:
[[1,6,9],[6,7,24],[7,8,15],[8,10,7]]
说明:
[1,6)
颜色{9}
[6,7)
颜色{9,15}
,和为24
[7,8)
颜色{15}
[8,10)
颜色{7}
示例3
输入:
segments = [[1,4,5],[1,4,7],[4,7,1],[4,7,11]]
输出:
[[1,4,12],[4,7,12]]
说明:
[1,4)
颜色{5,7}
, 和为12[4,7)
颜色{1,11}
, 和为12
解题分析
本题的关键是处理重叠区间及其颜色状态的变化。
画上的颜色混合可以看成一维数轴上多条线段的颜色叠加问题。由于线段有重叠,混合颜色集合是所有重叠颜色的并集,简化为颜色和。
任务是把画分割成最少数量的、颜色状态一致的区间。
核心挑战:
- 如何高效表示和更新颜色集合?
- 如何划分区间,使颜色集合不变的区间合并在一起?
- 处理区间边界的状态变化。
经典思路:扫描线(事件排序)
- 构造事件点:
对每个线段生成两个事件:- 线段开始点:颜色加入集合
- 线段结束点:颜色从集合移除
- 排序事件点:
按数轴位置排序,且同一点先处理颜色移除再处理颜色加入(保证状态在点之后更新)。 - 维护当前颜色和:
用变量curr_sum
维护当前颜色集合的元素和。 - 遍历事件点:
在相邻事件点之间,颜色状态不变,输出区间和颜色和。
细节说明
- 事件格式:
(位置, 颜色)
颜色加入为正数,颜色移除为负数,方便排序。 - 排序规则:
(位置, 颜色)
,负颜色先于正颜色,保证先移除后加入。 - 输出区间:
当扫描到一个新事件点pos
时,如果和上一个事件点prev_pos
不同且当前颜色和curr_sum
不为0,则输出[prev_pos, pos, curr_sum]
。
代码实现(Python)
from typing import List
class Solution:
def splitPainting(self, segments: List[List[int]]) -> List[List[int]]:
events = []
for start, end, color in segments:
events.append((start, color)) # 颜色加入
events.append((end, -color)) # 颜色移除
# 按坐标排序,颜色负数先于正数(先移除后加入)
events.sort(key=lambda x: (x[0], x[1]))
res = []
curr_sum = 0
prev_pos = None
for pos, color in events:
# 输出上一段区间
if prev_pos is not None and prev_pos != pos and curr_sum != 0:
res.append([prev_pos, pos, curr_sum])
# 更新颜色和
curr_sum += color
prev_pos = pos
return res
复杂度分析
- 时间复杂度:
主要是排序,O(N log N)
,其中N
为线段数。遍历事件线性。 - 空间复杂度:
事件数为2N
,存储和输出线性空间。
与其他方法的比较
- 暴力遍历每个点:
可能超时,尤其坐标范围最大到10^5。 - 线段树等区间结构:
复杂且实现难度大,且本题不需要支持动态查询。 - 扫描线事件法:
简洁有效,状态只在事件点发生变化,适合本题。
总结
本题通过扫描线算法解决,核心是将线段开始和结束作为事件点,动态维护当前颜色集合的和,输出颜色不变的区间。
实现简单,逻辑清晰,是处理区间重叠、颜色混合类问题的经典范式。