[LeetCode] 850. Rectangle Area II 矩形面积之二
We are given a list of (axis-aligned) `rectangles`. Each `rectangle[i] = [x1, y1, x2, y2] `, where (x1, y1) are the coordinates of the bottom-left corner, and (x2, y2) are the coordinates of the top-right corner of the `i`th rectangle.
Find the total area covered by all rectangles
in the plane. Since the answer may be too large, return it modulo 10^9 + 7.
Example 1:
Input: [[0,0,2,2],[1,0,2,3],[1,0,3,1]]
Output: 6
Explanation: As illustrated in the picture.
Example 2:
Input: [[0,0,1000000000,1000000000]]
Output: 49
Explanation: The answer is 10^18 modulo (10^9 + 7), which is (10^9)^2 = (-7)^2 = 49.
Note:
1 <= rectangles.length <= 200
rectanges[i].length = 4
0 <= rectangles[i][j] <= 10^9
- The total area covered by all rectangles will never exceed
2^63 - 1
and thus will fit in a 64-bit signed integer.
这道题是之前那道 [Rectangle Area](http://www.cnblogs.com/grandyang/p/4563153.html) 的拓展,那道题只有两个矩形重叠,而这道题有多个矩形可能同时重叠,整体难度一下就上来了,那么通过将所有矩形面积加起来再减去重叠区域的方法这里就不太适用了,因为多个矩形在同一区域重叠的话,都减去重叠面积是会错的,还得把多减的补回来,相当的麻烦。这里我们需要换一种解题的思路,不能一股脑儿的把所有的矩形都加起来,而是应该利用微积分的思想,将重叠在一起的区域拆分成一个个的小矩形,分别累加面积,因为这里的矩形是不会旋转的,所以是可以正常拆分的。思路有了,新建一个二维数组 all 来保存所有的矩形,然后遍历给定的矩形数组,对于每个遍历到的数组,调用一个子函数,将当前的矩形加入 all 中。下面主要来看一下这个子函数 helper 该如何实现?首先要明白这个函数的作用是将当前矩形加入 all 数组中,而且用的是递归的思路,所以要传入一个 start 变量,表示当前和 all 数组中正在比较的矩形的 index,这样在开始的时候,检查一下若 start 大于等于 all 数组长度,表示已经检测完 all 中所有的矩形了,将当前矩形加入 all 数组,并返回即可。否则的话则取出 start 位置上的矩形 rec,此时就要判断当前要加入的矩形和这个 rec 矩形是否有重叠,这在 LeetCode 中有专门一道题是考察这个的 [Rectangle Overlap](https://www.cnblogs.com/grandyang/p/10367583.html),这里用的就是那道题的判断方法,假如判断出当前矩形 cur 和矩形 rec 没有交集,就直接对 all 数组中下一个矩形调用递归函数,并返回即可。假如有重叠的话,就稍微麻烦一点,由于重叠的部位不同,所以需要分情况讨论一下,参见下图所示:
对于一个矩形 Rectangle,若有另外一个矩形跟它有重叠的话,可以将重叠区域分为四个部分,如上图的 Case1,Case2,Case3,Case4 所示,非重叠部分一定会落在一个或多个区域中,则可以把这些拆开的小矩形全部加入到矩形数组 all 中。仔细观察上图可以发现,对于将矩形 cur 拆分的情况可以分为下面四种:
- 落入区间1,条件为 cur[0] < rec[0],产生的新矩形的两个顶点为 {cur[0], cur[1], rec[0], cur[3]}。
- 落入区间2,条件为 cur[2] > rec[2],产生的新矩形的两个顶点为 {rec[2], cur[1], cur[2], cur[3]}。
- 落入区间3,条件为 cur[1] < rec[1],产生的新矩形的两个顶点为 {max(rec[0], cur[0]), cur[1], min(rec[2], cur[2]), rec[1]}。
- 落入区间4,条件为 cur[3] > rec[3],产生的新矩形的两个顶点为 {max(rec[0], cur[0]), rec[3], min(rec[2], cur[2]), cur[3]}。
这样操作下来的话,整个所有的区域都被拆分成了很多个小矩形,每个矩形之间都不会有重复,最后只要分别计算每个小矩形的面积,并累加起来就是最终的结果了,参见代码如下:
解法一:
class Solution {
public:
int rectangleArea(vector<vector<int>>& rectangles) {
long res = 0, M = 1e9 + 7;
vector<vector<int>> all;
for (auto rectangle : rectangles) {
helper(all, rectangle, 0);
}
for (auto &a : all) {
res = (res + (long)(a[2] - a[0]) * (long)(a[3] - a[1])) % M;
}
return res;
}
void helper(vector<vector<int>>& all, vector<int> cur, int start) {
if (start >= all.size()) {
all.push_back(cur); return;
}
auto rec = all[start];
if (cur[2] <= rec[0] || cur[3] <= rec[1] || cur[0] >= rec[2] || cur[1] >= rec[3]) {
helper(all, cur, start + 1); return;
}
if (cur[0] < rec[0]) {
helper(all, {cur[0], cur[1], rec[0], cur[3]}, start + 1);
}
if (cur[2] > rec[2]) {
helper(all, {rec[2], cur[1], cur[2], cur[3]}, start + 1);
}
if (cur[1] < rec[1]) {
helper(all, {max(rec[0], cur[0]), cur[1], min(rec[2], cur[2]), rec[1]}, start + 1);
}
if (cur[3] > rec[3]) {
helper(all, {max(rec[0], cur[0]), rec[3], min(rec[2], cur[2]), cur[3]}, start + 1);
}
}
};
下面这种解法更是利用了微积分的原理,把x轴长度为1当作一个步长,然后计算每一列有多少个连续的区间,每个连续区间又有多少个小正方形,题目中给的例子每一个列都只有一个连续区间,但事实上是可以有很多个的,只要算出了每一列 1x1 小正方形的个数,将所有列都累加起来,就是整个区域的面积。这里求每列上小正方形个数的方法非常的 tricky,博主也不知道该怎么讲解,大致就是要求同一列上每个连续区间中的小正方形个数,再累加起来。对于每个矩形起始的横坐标,映射较低的y值到1,较高的y值到 -1,对于结束位置的横坐标,刚好反过来一下,映射较低的y值到 -1,较高的y值到1。这种机制跟之前那道 [The Skyline Problem](http://www.cnblogs.com/grandyang/p/4534586.html) 有些异曲同工之妙,都还是为了计算高度差服务的。要搞清楚这道题的核心思想,不是一件容易的事,博主的建议是就拿题目中给的例子带入到下面的代码中,一步一步执行,并分析结果,是能够初步的了解解题思路的,若实在有理解上的问题,博主可以进一步写些讲解,参见代码如下:
解法二:
class Solution {
public:
int rectangleArea(vector<vector<int>>& rectangles) {
long res = 0, pre_x = 0, height = 0, start = 0, cnt = 0, M = 1e9 + 7;
map<int, vector<pair<int, int>>> groupMap;
map<int, int> cntMap;
for (auto &a : rectangles) {
groupMap[a[0]].push_back({a[1], 1});
groupMap[a[0]].push_back({a[3], -1});
groupMap[a[2]].push_back({a[1], -1});
groupMap[a[2]].push_back({a[3], 1});
}
for (auto &group : groupMap) {
res = (res + (group.first - pre_x) * height) % M;
for (auto &a : group.second) {
cntMap[a.first] += a.second;
}
height = 0, start = 0, cnt = 0;
for (auto &a : cntMap) {
if (cnt == 0) start = a.first;
cnt += a.second;
if (cnt == 0) height += a.first - start;
}
pre_x = group.first;
}
return res;
}
};
Github 同步地址:
https://github.com/grandyang/leetcode/issues/850
类似题目:
参考资料:
https://leetcode.com/problems/rectangle-area-ii/
https://leetcode.com/problems/rectangle-area-ii/discuss/138028/Clean-Recursive-Solution-Java
[LeetCode All in One 题目讲解汇总(持续更新中...)](https://www.cnblogs.com/grandyang/p/4606334.html)
[LeetCode] 850. Rectangle Area II 矩形面积之二的更多相关文章
- leetcode 850. Rectangle Area II
给定一些矩形2 求覆盖面积 矩形不超过200个 1 算法1 朴素思想 虽然朴素但是代码却有意思 利用容斥原理 复杂度高达 N*2^N class Solution: def intersect(rec ...
- LeetCode 223 Rectangle Area(矩形面积)
翻译 找到在二维平面中两个相交矩形的总面积. 每一个矩形都定义了其左下角和右上角的坐标. (矩形例如以下图) 如果,总占地面积永远不会超过int的最大值. 原文 分析 这题前天试过,写了一堆推断.终究 ...
- [LeetCode] 223. Rectangle Area 矩形面积
Find the total area covered by two rectilinearrectangles in a 2D plane. Each rectangle is defined by ...
- [LeetCode]223. Rectangle Area矩形面积
/* 像是一道数据分析题 思路就是两个矩形面积之和减去叠加面积之和 */ public int computeArea(int A, int B, int C, int D, int E, int F ...
- (easy)LeetCode 223.Rectangle Area
Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is defined b ...
- [Swift]LeetCode850. 矩形面积 II | Rectangle Area II
We are given a list of (axis-aligned) rectangles. Each rectangle[i] = [x1, y1, x2, y2] , where (x1, ...
- leetcode之Rectangle Area
Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is defined b ...
- leetcode:Rectangle Area
Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is defined b ...
- LeetCode(41)-Rectangle Area
题目: Find the total area covered by two rectilinear rectangles in a 2D plane. Each rectangle is defin ...
随机推荐
- Java连载7-变量&数据类型
一.变量 1.注意点: 在同一个“作用域”中,变量名不能重名,但是变量可以重新赋值. 2.什么是作用域? 答:描述的是变量的有效范围,在范围之内是可以被访问的,只要出了作用域就无法访问(也就是在大括号 ...
- SQL Server 2014:为什么数据库里的表提示“单元格是只读的”,不能修改?该如何处理?
出现以上这种情况,首先看一下这个字段的属性“标识规范”是不是选了“是”,自增属性下是不能修改的,属于只读.
- Java单元测试简述
最开始项目中是没有单元测试的,基本都是自己通过各种方式来实现测试的.比如修改代码,测完再改回来:再比如直接模拟用户操作,直接当黑盒测试,然后自己去看相应的逻辑有没有,状态有没有改变. 这些方式有几个缺 ...
- Winform 美化
首先,我们先来实现主界面的扁平化 此处分为两个步骤,第一步是更改winform自带的MainForm窗体属性,第二步是添加窗体事件. 将主窗体FormBorderStyle更改为None,这样就得到了 ...
- ABP中文网的一些BUG
之前一些翻译了的文档没有及时更新.比如 IAsyncCrudAppService接口在很久之前的版本就已经改为了ICrudAppService,如果是在官网下载的最新实例中IAsyncCrudAppS ...
- SVN每日定时备份脚本
SVN每日定时备份脚本: @ECHO off REM SVN安装目录 SET SVN_HOME="D:\Program Files\VisualSVNServer" REM 版本库 ...
- CSS自定义右键菜单
<style> .menu { width: 100px; font-size: 14px; font-family: "微软雅黑"; border: 1px soli ...
- 如何使用Postman发送get请求?
一.接口测试介绍 接口测试:就是针对软件对外提供服务的接口输入输出进行测试,以及接口间相互逻辑的测试,验证接口功能和接口描述文档的一致性. 接口测试好处:接口测试通常能对系统测试的更为彻底,更高的保障 ...
- jQuery中的index用法与inArray用法
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- 初识V4l2(二)-------浅析video_register_device
在V4l2初识(一)中,我们已经知道当插上一个摄像头的时候,在uvc_driver.c中最终会调用函数video_register_device函数.接下来我们就简要分析这个函数做了哪些事情,揭开其神 ...