codeforces-go 算法模板库实战:LeetCode 双周赛 146「矩形切分」题解——二维切割降维成一维区间合并计数

发布时间:2026/10/5 10:22:36

codeforces-go 算法模板库实战:LeetCode 双周赛 146「矩形切分」题解——二维切割降维成一维区间合并计数 科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载本篇技术指南围绕 LeetCode 双周赛 146 第三题「判断网格是否可以分成三个区域Check if Grid Can Be Cut Into Sections」展开以算法竞赛模板库 codeforces-go 中 leetcode/biweekly/146/c/README.md 的官方题解为骨架结合仓库内 c.go 的 Go 实现、c_test.go 与 c.txt 的测试用例以及模板库中的区间合并工具 mergeIntervals完整讲解矩形区域切分问题如何降维成区间合并计数问题的思考过程与多语言代码实现。读完本文你将掌握一类高频套路把二维几何问题按坐标轴投影降维为一维区间问题再通过排序 单次扫描统计连通块数量并能直接复用文中代码与测试框架验证自己的解法。一、题目背景矩形网格能否被两条直线切成三段本题是 LeetCode 第 146 场双周赛的 Q3力扣原题名为Check if Grid Can Be Cut Into Sections中文通常译为判断网格是否可以分成三个区域仓库中对应目录为 leetcode/biweekly/146/c与同场其他三题一同收录在 leetcode/biweekly/146/README.md。题目描述要点归纳给定 $n$ 个矩形每个矩形用[startX, startY, endX, endY]表示注意是end而不是right/bottom问是否存在两条水平线或两条竖直线能把所有矩形所在的整个区域不穿过任何矩形内部地切成三部分。题目的关键限定是切割线必须落在矩形之间的空隙里即切割不能与任何矩形相交。这正是把问题转化为区间的契机。二、核心思路按坐标轴投影把矩形降维成区间2.1 竖切时答案与纵坐标矩形的高无关原文档给出第一个关键观察竖切的时候答案与纵坐标也就是矩形的高无关我们可以把每个矩形视作一个区间 $[\textit{start}_x, \textit{end}_x]$。竖直线只能切在 $x$ 方向的空隙上而矩形在 $x$ 方向的跨度只取决于 $\textit{start}_x$ 和 $\textit{end}_x$。因此每个矩形被压缩成 $x$ 轴上的一条闭区间 $[\textit{start}_x, \textit{end}_x]$问题就变成把这 $n$ 个区间合并后区间的个数是否 $\ge 3$。这里用到的背景知识正是经典的 56. 合并区间 套路排序 单次扫描。需要注意本题的合并语义是端点相接不算重叠——如果上一个区间右端点和下一个区间左端点恰好相等中间夹的是宽度为零的线切割线并不能从那里穿过矩形边界相邻所以两个区间应当算作同一个连通块。2.2 横切完全对称横切同理把每个矩形视作一个区间 $[\textit{start}_y, \textit{end}_y]$。即每个矩形再压缩成 $y$ 轴上的一条区间合并后判断区间个数是否 $\ge 3$。竖切可行或横切可行二者满足其一即返回true因此最终答案是checkValidCuts check(按 x 投影的区间) OR check(按 y 投影的区间)2.3 为什么合并后的区间个数 ≥ 3等价于能切两刀一条切割线必须落在两个相邻连通块之间。若能切出两刀且切成三段那么至少需要存在三个互不相交不相重叠的连通块反过来若合并后存在 $\ge 3$ 个连通块我们只需在其中两个相邻连通块的间隙处各放一条直线即可横切用水平线、竖切用竖直线且这条线必然不穿过任何矩形内部。这就是计数判断的充要性。三、多语言实现排序 单次扫描统计连通块原文档提供了 Python3、Java、C、Go 四套完整可运行代码核心都是一个check函数按左端点排序然后一次遍历统计新区间的数量。下面逐一继承并给出逐行注释。3.1 Python3class Solution: def check(self, intervals: List[Tuple[int, int]]) - bool: intervals.sort(keylambda p: p[0]) # 按照左端点从小到大排序 cnt max_r 0 for l, r in intervals: if l max_r: # 新区间 cnt 1 if r max_r: max_r r # 更新右端点最大值手写 if 效率更高 return cnt 3 # 也可以在循环中提前退出但是慢一些 def checkValidCuts(self, _: int, rectangles: List[List[int]]) - bool: return self.check([(sx, ex) for sx, _, ex, _ in rectangles]) or \ self.check([(sy, ey) for _, sy, _, ey in rectangles])3.2 Javaclass Solution { boolean checkValidCuts(int n, int[][] rectangles) { int m rectangles.length; int[][] a new int[m][2]; int[][] b new int[m][2]; for (int i 0; i m; i) { int[] rect rectangles[i]; a[i][0] rect[0]; a[i][1] rect[2]; b[i][0] rect[1]; b[i][1] rect[3]; } return check(a) || check(b); } private boolean check(int[][] intervals) { Arrays.sort(intervals, (a, b) - a[0] - b[0]); // 按照左端点从小到大排序 int cnt 0; int maxR 0; for (int[] interval : intervals) { if (interval[0] maxR) { // 新区间 cnt; } maxR Math.max(maxR, interval[1]); // 更新右端点最大值 } return cnt 3; } }3.3 Cclass Solution { public: bool check(vectorpairint, int intervals) { ranges::sort(intervals, {}, [](auto a) { return a.first; });// 按照左端点从小到大排序 int cnt 0, max_r 0; for (auto [l, r] : intervals) { if (l max_r) { // 新区间 cnt; } max_r max(max_r, r); // 更新右端点最大值 } return cnt 3; // 也可以在循环中提前退出 } bool checkValidCuts(int, vectorvectorint rectangles) { vectorpairint, int a, b; for (auto rect : rectangles) { a.emplace_back(rect[0], rect[2]); b.emplace_back(rect[1], rect[3]); } return check(a) || check(b); } };3.4 Go仓库 c.go 的完整实现仓库 c.go 中的实现与原文档的 Go 版本完全一致也是本仓库为本题保留的官方 AC 代码依赖 Go 1.21 的slices包与内置maxpackage main import slices type pair struct{ l, r int } func check(intervals []pair) bool { // 按照左端点从小到大排序 slices.SortFunc(intervals, func(a, b pair) int { return a.l - b.l }) cnt, maxR : 0, 0 for _, p : range intervals { if p.l maxR { // 新区间 cnt } maxR max(maxR, p.r) // 更新右端点最大值 } return cnt 3 // 也可以在循环中提前退出 } func checkValidCuts(_ int, rectangles [][]int) bool { a : make([]pair, len(rectangles)) b : make([]pair, len(rectangles)) for i, rect : range rectangles { a[i] pair{rect[0], rect[2]} b[i] pair{rect[1], rect[3]} } return check(a) || check(b) }3.5 check 函数逐行推演以 Go 版为例check的核心逻辑可以拆成三步这正是合并区间计数的标准扫描法排序slices.SortFunc按左端点l升序排列保证后续扫描时新区间只可能出现在更右的位置维护最大右端点maxR它是当前连通块向右能覆盖到的最远位置判定新区间若当前区间的左端点l maxR说明它与之前所有区间都无重叠相接同样视为新连通块因为重合的边界线无法供切割穿过连通块计数cnt随后用max(r, maxR)更新最右覆盖。需要注意l maxR用的是而不是当l maxR时两个区间首尾相接中间没有空隙不能从那里下刀因此它们属于同一个连通块。这正是本题与标准合并区间在细节上的一致性。checkValidCuts中两次构造区间数组a[i] pair{rect[0], rect[2]}提取 $x$ 方向投影b[i] pair{rect[1], rect[3]}提取 $y$ 方向投影两路任一通过即返回true。四、复杂度分析原文档给出的复杂度结论如下时间复杂度$\mathcal{O}(m\log m)$其中 $m$ 是 $\textit{rectangles}$ 的长度即矩形个数瓶颈在排序上排序后的扫描是线性 $\mathcal{O}(m)$。空间复杂度$\mathcal{O}(m)$主要用于存放投影后的两个区间数组Go 实现中a、b各占 $\mathcal{O}(m)$也可复用原数组以减少额外内存但实现上直接开辟新数组更清晰。五、仓库中的测试验证从测试文件到通用测试框架5.1 测试文件与用例数据本题在仓库中配有完整测试c_test.go 通过调用testutil.RunLeetCodeFuncWithFile(t, checkValidCuts, c.txt, 0)逐组跑通 c.txt 中的所有用例测试数据按参数行 期望输出行成组排列5 [[1,0,5,2],[0,2,2,4],[3,2,5,3],[0,4,4,5]] true 4 [[0,0,1,1],[2,0,3,4],[0,2,2,3],[3,0,4,3]] true 4 [[0,2,2,4],[1,0,3,2],[2,2,3,4],[3,0,4,2],[3,2,4,4]] false注意第三个用例的答案是false虽然矩形在 $x$ 方向投影为[0,3],[1,3],[2,3],[3,4]合并后只有两个连通块竖切不可行$y$ 方向投影[2,4],[0,2],[2,4],[0,2]合并后同样只有两个连通块横切也不可行故不能切成三段。用它来验证合并后区间个数 ≥ 3的判定逻辑非常直观。5.2 底层测试框架怎么工作RunLeetCodeFuncWithFile实现在 leetcode/testutil/leetcode.go它按函数签名反射出参数个数fNumIn与返回值个数fNumOut把c.txt每fNumInfNumOut行切分为一组样例再交给RunLeetCodeFuncWithExamples逐个执行并比对输出AssertOutput开启时用断言逐用例校验。若用例数据不合法如行数不是组大小的整数倍框架会直接返回错误。这套测试文件 反射驱动的机制是仓库所有 LeetCode 题解共用的验证通道由 copypasta/template/leetcode/generator_test.go 中的生成器配合爬取题面自动产出。运行测试只需在仓库根目录执行go test ./leetcode/biweekly/146/c/六、与模板库 mergeIntervals 的对照为什么这里不需要真的合并算法模板库中其实已经内置了标准的区间合并工具 mergeIntervals注释标注关联力扣 56 题并列举了 Codeforces 1101C、1626C、1859D、1260D 等应用场景func mergeIntervals(a [][]int) [][]int { slices.SortFunc(a, func(a, b []int) int { return a[0] - b[0] }) // 按照左端点从小到大排序 merged : [][]int{} left, right : math.MaxInt, math.MinInt for i, p : range a { left min(left, p[0]) right max(right, p[1]) if i len(a)-1 || a[i1][0] right { // [1,1] 和 [2,2] 不合并 merged append(merged, []int{left, right}) left math.MaxInt } } return merged }对比可见mergeIntervals关注的是输出合并后的区间集合因此需要维护当前块的左右边界并不断append结果本题的check只关心合并后连通块的个数是否 ≥ 3所以不必真正产出合并后的区间只需维护当前连通块的最远右端点maxR并在发现新连通块时cnt一旦cnt 3即可提前得出结论原文档注释也提到可以在循环中提前退出只是略慢的写法是扫描完再判断。这是合并区间套路的两种形态求并集 vs 求个数。理解两者的差异就能在面对判断能否切分/覆盖/分离一类问题时快速写出最精简的扫描代码。七、从一道题到一个套路二维切分问题的通用解法框架综合以上分析本题的完整解题链条可抽象为三步适用于所有能否用直线/隔板把图形区域切开类问题降维投影切割方向确定后只保留与该方向垂直的坐标跨度竖切看 $x$、横切看 $y$矩形退化为闭区间区间化判定区间合并后连通块个数决定可切刀数可切 $k-1$ 刀需要 $k$ 个互不重叠的连通块线性扫描排序后一遍遍历用最大右端点比较是否开新块全程 $\mathcal{O}(m\log m)$。原文档末尾还附有一份覆盖滑动窗口与双指针、二分算法、单调栈、网格图、位运算、图论算法、动态规划、常用数据结构、数学算法、贪心与思维、链表二叉树与回溯、字符串等 12 个方向的分类题单用于把同类套路集中练习、加深印象。本文所讲的合并区间计数正是其中贪心与思维/区间与常用数据结构/栈队列交叉处的高频考点值得在模板库的 copypasta/common.go另见其中的区间贪心注释基础上反复演练。赞分享科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载相关推荐codeforces-go 算法模板库实战解析LeetCode 双周赛 142 Q1「相邻相同字母对数 1」的思维题解法与 Go 工程化测试codeforces go 算法模板库实战解析LeetCode 双周赛 142 Q1「相邻相同字母对数 1」的思维题解法与 Go 工程化测试 本篇技术指南科学计算「唯一中间众数」子序列计数从 O(n²) 分类枚举到 O(n) 式子变形 —— codeforces-go 仓库 LeetCode 双周赛 146 Q4 题解精讲「唯一中间众数」子序列计数从 O n² 分类枚举到 O n 式子变形 —— codeforces go 仓库 LeetCode 双周赛 146 Q4 题解精讲科学计算codeforces-go 实战题解第二类换根 DP维护最大次大——以 LeetCode 双周赛 136 第四题 timeTaken 为例codeforces go 实战题解第二类换根 DP维护最大次大——以 LeetCode 双周赛 136 第四题 timeTaken 为例 本文基于 co科学计算上一篇grok-build Prompt History 会话级作用域改造Up 箭头 / CtrlR 仅显示当前会话提示词下一篇gbrain Brain-First Lookup 协议先查大脑、再问外部 API 的 Agent 检索规范创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/5 11:32:40

C#仓库条码管理系统实战:扫码枪接入、库存流水与打印状态机

简介:一份基于 C# 的仓库条码管理系统毕业设计源码,面向计算机相关专业学生及初阶仓库管理开发者。系统采用 Windows Forms 构建界面,围绕条形码扫描实现入库、出库、库存查询与报表生成等核心模块,有助于理解 C# 面向对象编程、窗…

2026/10/5 11:32:40

GESP六级树的遍历:从递归序到非递归,再到还原二叉树

树的遍历,在GESP六级大纲里就像是树这个章节的“敲门砖”。我带过的很多学生,最初都觉得不过就是三种递归写法嘛,背下来就完了,结果到了考场上,一道“已知中序和后序,让你求前序”直接傻眼,或者…

2026/10/5 11:32:40

一文看懂Linux文件类型:从ls -l到inode,彻底搞清七种类型

刚接手一台陌生的 Linux 服务器,或者第一次打开某个开源项目的源码目录时,我几乎都会敲一遍ls -l。这一敲,第一列那一串十个字符,就是整个文件系统的"身份证明"。很多新手盯着drwxr-xr-x、-rw-r--r--发呆,只…

2026/10/5 11:32:40

无摩擦支付:消费双刃剑与实操止损清单

支付越“丝滑”,花钱越“随意”?这份报告把无摩擦支付的消费双刃剑讲透了——附实操止损清单 作为一个和支付产品打了多年交道的人,我太熟悉“无摩擦支付”这个词了。从最初的密码输入,到指纹支付、刷脸支付,再到现在…

2026/10/5 11:32:40

插件加载失败排查:从‘did not activate‘到系统化解决

去年年中我在维护一个内部工具平台的插件模块时,几乎每天都会被类似这样的报错信息折磨:"failed to load plugins web boot: 2 entries did not activate"、"harness failed to load plugins web boot: 1 entry did not activate huayu-y…

2026/10/5 11:27:40

MATLAB/Simulink雷达仿真实战:从信号建模到目标检测

1. 为什么非要用 MATLAB/Simulink 做雷达仿真1.1 雷达仿真到底在仿什么先聊个实际的场景。我去年接手了一个车载毫米波雷达的项目,硬件平台还没到位,算法团队天天喊着要调参,测试场地排期又遥遥无期。当时如果没有一套趁手的仿真工具&#xf…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/4 0:01:02

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/4 1:01:05

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑