1943. 描述绘画结果

画的颜色混合与线段合并 —— 「最少区间数表示混合颜色」问题解析与实现


题目描述

给定一个细长的画,抽象为数轴上的若干线段,每个线段带有独一无二的颜色编号。线段之间可能重叠,重叠部分的颜色会“混合”,混合颜色用集合表示,集合中元素是所有重叠线段的颜色。

为了简化,集合颜色用集合元素之和来表示。

输入是二维整数数组 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

解题分析

本题的关键是处理重叠区间及其颜色状态的变化

画上的颜色混合可以看成一维数轴上多条线段的颜色叠加问题。由于线段有重叠,混合颜色集合是所有重叠颜色的并集,简化为颜色和。

任务是把画分割成最少数量的、颜色状态一致的区间


核心挑战:

  • 如何高效表示和更新颜色集合?
  • 如何划分区间,使颜色集合不变的区间合并在一起?
  • 处理区间边界的状态变化。

经典思路:扫描线(事件排序)

  1. 构造事件点
    对每个线段生成两个事件:
    • 线段开始点:颜色加入集合
    • 线段结束点:颜色从集合移除
  2. 排序事件点
    按数轴位置排序,且同一点先处理颜色移除再处理颜色加入(保证状态在点之后更新)。
  3. 维护当前颜色和
    用变量 curr_sum 维护当前颜色集合的元素和。
  4. 遍历事件点
    在相邻事件点之间,颜色状态不变,输出区间和颜色和。

细节说明

  • 事件格式: (位置, 颜色)
    颜色加入为正数,颜色移除为负数,方便排序。
  • 排序规则:
    (位置, 颜色),负颜色先于正颜色,保证先移除后加入。
  • 输出区间:
    当扫描到一个新事件点 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。
  • 线段树等区间结构
    复杂且实现难度大,且本题不需要支持动态查询。
  • 扫描线事件法
    简洁有效,状态只在事件点发生变化,适合本题。

总结

本题通过扫描线算法解决,核心是将线段开始和结束作为事件点,动态维护当前颜色集合的和,输出颜色不变的区间。

实现简单,逻辑清晰,是处理区间重叠、颜色混合类问题的经典范式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值