当前位置: 首页 > news >正文

线段的最少分组

1 - 模型描述

在数轴上,有 \(n\) 条线段,对于第 \(i\) 条线段覆盖的区间为 \([l_i, r_i]\) ,现在你需要对这 \(n\) 条线段按如下的规则进行分组:

  • 同一组的线段之间互不重叠,即对于同一组的线段 \(i, j\) 存在 \(l_j > r_i\)\(r_j < l_i\).

求最少需要分多少组,并输出分组方案。

2 - 结论 & 证明 & 做法

结论:

最小分组等于某点上最大线段覆盖数量。

设最大线段覆盖数量为 \(M\).

证明:

  • 下界:任取一点 \(x\),覆盖 \(x\) 的所有区间两两重叠,必须被分到不同组。因此最少组数 \(\geq\) 覆盖 \(x\) 的线段数。取被覆盖最多的 \(x\),最少组数 \(\geq\) 最大覆盖数 \(M\).
  • 上界:将所有线段按左端点升序排列,维护每一个已建组的 “最大右端点” 集合。
    • 若当前线段的左端点 \(>\) 某一组的最大右端点,则把该线段加入这一组,并更新这一组的最大右端点。
    • 否则为该线段新开一组。
  • 当不得不新开一组时,说明所有已有组的最大右端点都 \(\geq\) 当前线段的左端点 \(l\). 于是:
    • 这些已有组各自的最靠右的线段都覆盖点 \(l\);
    • 加上当前线段也覆盖 \(l\);
    • 所以在点 \(l\) 处至少有(当前分组数 + 1)条线段同时覆盖,覆盖数 \(\geq\) 已建组数 + 1.
  • 推论:在分组过程的 “分组数量” 每增长 1,都能在某个点被覆盖数 “见证”。因此在过程中的任意时刻的分组数量 \(\leq\) 最大覆盖数量 \(M\).
  • 将上下界结合得到,最少组数 = 最大覆盖数量 \(M\).

做法:

  • 思路:按线段左端点 \(l_i\) 升序贪心。维护 “当前已建分组” 的最右端点。
  • 使用小根堆或有序集合维护当前线段是否需要新开一组,同时维护分组方案。
    • 小根堆:\(pair\) 存当前组的最右端点,组号。处理线段 \([l_i, r_i]\) 时:
      • 若堆顶的 \(r < l_i\) 则将此线段加入该组;
      • 否则新开一组。
    • 有序集合:\(pair\) 存当前组的最右端点,组号。每次 lower_bound({l_i, -1}) 找到第一个 \(r \geq l_i\) 的位置,若存在前驱则使用前驱所在组,否则新开一组。
  • 复杂度 \(O(n \cdot logn)\)

3 - 例题 - CSES1164

题目描述:

这里有一家酒店, 和 \(n\) 为即将到达的客人. 每一位客人都需要一间单独的房间。

你知道每位客人的到达日期和离开日期。两位客人只有在第一位到达的客人离开的时间早于第二位客人到达的时间时,才会被分配到同一间房间。

我们最少需要为所有客人总共准备多少房间? 以及如何分配房间?

输入格式:

第一行输入一个整数 \(n\) \((1 \le n \le 2 \cdot 10^5)\): 顾客的数量。

接下来的 \(n\) 行, 每行描述一位顾客。 每一行输入两个整数 \(a\)\(b\) \((1 \le a \le b \le 10^9)\): 到达日期与离开日期。

输出格式:

第一行输出一个整数 \(k\): 最小需要客房数量。

接下来的一行,依次输出为每一位顾客分配的房间号。房间号必须为 \(1,2,\ldots,k\). 你可以输出任意答案。

思路:

  • 将每一位客人的行程想象成线段,左端点为到达日期,右端点为离开日期。
  • 然后使用模型方法解决即可。

参考代码:

#include <bits/stdc++.h>
using namespace std;#define endl '\n'
#define i64 long longstruct Customer{int arrive, leave, number;
};int main() {ios::sync_with_stdio(false);cin.tie(nullptr), cout.tie(nullptr);int n;cin >> n;vector<Customer> cus(n);for (int i = 0; i < n; i ++ ) {cin >> cus[i].arrive >> cus[i].leave;cus[i].number = i;}ranges::sort(cus, [&](auto a, auto b) {return a.arrive < b.arrive;});int cnt = 0;vector<int> ans(n);set<pair<int, int>> leaveTime;for (int i = 0; i < n; i ++ ) {int arrive = cus[i].arrive;int leave = cus[i].leave;int number = cus[i].number;auto it = leaveTime.lower_bound({arrive, -1});if (it == leaveTime.begin()) {ans[number] = ++cnt;leaveTime.insert({leave, cnt});}else {--it;ans[number] = it->second;leaveTime.erase(it);leaveTime.insert({leave, ans[number]});}}cout << cnt << endl;for (int i = 0; i < n; i ++ ) {cout << ans[i] << " \n"[i == n - 1];}
}

4 - 练习

NC201994: https://ac.nowcoder.com/acm/problem/201994

http://www.jsqmd.com/news/71201/

相关文章:

  • 【Ubuntu】系统下VScode配置ESP-IDF插件esp-clang和Python 3报错问题
  • 新房装修不迷路!十大公司深度评测,盛世和家登顶榜首 - 品牌测评鉴赏家
  • windriver 第6章:使用DriverWizard
  • vue 中支持不定高的虚拟滚动的表格 vxe-table 的使用,动态高度虚拟列表高性能表格
  • windriver 第4章:PCI Express 概述
  • GROMACS 2025.4安装(非root用户)
  • MATLAB R2025a免费下载安装教程与激活教程超详细图文步骤(附安装包)
  • 解码string类——字符串处理
  • 新房装修公司大揭秘!一文教你避坑选好公司 - 品牌测评鉴赏家
  • 新手装修必看!第一次选对装修公司,省心攻略全解析 - 品牌测评鉴赏家
  • Docker Swarm 的负载均衡和平滑切换原理 - 实践
  • windriver 第3章: 安装WinDriver
  • 2025年推荐实力户外滑梯厂家飞友,以专业品质守护儿童欢乐时光 - 速递信息
  • 2025整装公司排行榜发布:十大整装品牌引领行业新趋势 - 速递信息
  • day3 Java基础3
  • 头歌MySQL——单表查询 - 详解
  • windriver 第2章: 了解设备驱动程序
  • 纯棉卫生巾推荐,4款热门产品深度横评,看完这篇再下单! - 速递信息
  • 2025年整装公司口碑推荐实力TOP榜|十大装修公司对比 - 速递信息
  • 2025年整装公司权威推荐榜:十大特色装修公司满足不同需求 - 速递信息
  • 2025整装公司排名榜!十强家装品牌核心优势对比 - 速递信息
  • 解决IDEA中项目目录的底色变黄
  • 2025年最新幼儿园教玩具品牌推荐,守护孩子成长——飞友用硬核筑牢成长防线 - 速递信息
  • 全屋整装公司品牌十强有哪些?2025排名与品牌解析 - 速递信息
  • 吐血整理!揭秘2025年新房装修公司哪家靠谱! - 品牌测评鉴赏家
  • 第五十九篇
  • 创建用户赋予权限
  • 2025 最新实测:AI 学习机是智商税吗?有没有用 + 高性价比品牌清单 - 品牌测评鉴赏家
  • AI 学习机品牌推荐(2025 年 12 月最新) 高性价比机型选购指南 - 品牌测评鉴赏家
  • 任意地址写basectf_format_string_level1