题目 850. 矩形面积 II
难度 困难
我们给出了一个(轴对齐的)矩形列表 rectangles 。 对于 rectangle[i] = [x1, y1, x2, y2],
其中(x1,y1)是矩形 i 左下角的坐标,(x2,y2)是该矩形右上角的坐标。
找出平面中所有矩形叠加覆盖后的总面积。 由于答案可能太大,请返回它对 10 ^ 9 + 7 取模的结果。
示例1:
输入:[[0,0,2,2],[1,0,2,3],[1,0,3,1]]
输出:6
示例2:
输入:[[0,0,1000000000,1000000000]]
输出:49
解释:答案是 10^18 对 (10^9 + 7) 取模的结果, 即 (109)2 → (-7)^2 = 49 。
提示:
- 1 <= rectangles.length <= 200
- rectanges[i].length = 4
- 0 <= rectangles[i][j] <= 10^9
- 矩形叠加覆盖后的总面积不会超越 2^63 - 1 ,这意味着可以用一个 64 位有符号整数来保存面积结果。
刚开始拿到这道题,我想了15分钟左右,没想出来怎么弄,想通过最笨的方法一个一个扫描然后计算总面积,发现重叠的部分不知道怎么去处理,所以关键就是如何去处理才能够重复计算重叠的面积。官方的解法写的我有些实在看不懂,这里给出别人比较好理解的解法
解法1 - 扫描法
如果我们是顺序处理rectangles,每次处理一个矩形,那么重复计算将变得非常难以处理。为此我们将矩形面价按照x轴(或者y轴)进行切割,然后再在y轴(或者x轴)进行累计求和。下面将选择在x轴方向进行切割。
算法描述:
第一步:将所有矩形的x坐标放入xVec
第二步:按照升序排序x坐标
第三步:在xVec中进行去重
第四步:逆序扫描x坐标段,计算在(xVec[i], xVec[i + 1])段中的面积假设xVec[i] = 1, xVec[i + 1] = 2,则所
有矩形在这个x区间段中存在交集的矩形y轴段是矩形[0,0,2,2]中的Y轴区间段[0, 2],矩形
[1,0,2,3]中的Y轴区间段[0, 3],矩形[1,0,3,1]中的Y轴区间段[0,1],合并时候Y轴区间段段为[0, 3]
(类似区间的合并)distanceX = xVec[i + 1] - xVec[i] = 1, distanceY = 3 - 0 = 3,所以S[2] =
distanceX * distanceY = 3
c++ 代码
class Solution {
public:
int rectangleArea(vector<vector<int>>& rectangles) {
vector<int> xVec;//用于存放所有矩形的x坐标
int sumArea = 0, mod = 7 + 1e9;
//第一步:将所有矩形的x坐标放入xVec
for (const auto &rectangle : rectangles){
xVec.push_back(rectangle[0]);
xVec.push_back(rectangle[2]);
}
//第二步:按照升序排序x坐标
sort(xVec.begin(), xVec.end());
//第三步:在xVec中进行去重(unique是STL中的去重函数,它会把重复的元素移动到xVec的后端,并且返回第一个重复的值的迭代器)
xVec.erase(unique(xVec.begin(), xVec.end()), xVec.end());
//第四步:逆序扫描x坐标段,计算在(xVec[i], xVec[i + 1])段中的面积
for (int i = xVec.size() - 2; i >= 0; --i){
list<pair<int, int>> myList;//按照递增的顺序存储所有不重复、重叠的y轴坐标段
//如果rectangle在(xVec[i], xVec[i + 1])段中有面积,则将它两个y轴坐标放入list
for (const auto &rectangle : rectangles){
if (rectangle[0] <= xVec[i] && rectangle[2] >= xVec[i + 1]){
addRange(myList, rectangle[1], rectangle[3]);
}
}
//然后我们需要把y轴这些区间段的长度进行求和
long distanceY = 0;
for (const auto &item : myList){ //auto关键字可以自动判断数据类型
distanceY += item.second - item.first;
}
//xVec[i + 1] - xVec[i]表示x轴区间段的宽度,distanceY是在这个x轴段y轴各个区间段长度的和
sumArea = (sumArea + (xVec[i + 1] - xVec[i]) * distanceY) % mod;
}
return sumArea;
}
//添加Range[left, right]
void addRange(list<pair<int, int>> &myList, int left, int right) {
auto it = myList.begin();
//第一步:确定[left,right]插入的位置(第一个与它有交集的元素)
while (it != myList.end() && it->second < left) {
// it->second 就是 rectangle[3]
++it;
}
//第二步:判断是插入到list中还是修改list
//如果是插入尾端、或者两个Range之间(list中没有Range与它有交集),直接插入
if (it == myList.end() || it->first > right) {
//it->firse 就是 rectangle[1]
myList.insert(it, { left, right});
}
else {
//此时修改it指向的元素
it->first = min(it->first, left);
it->second = max(it->second, right);
//然后看it后面是否有需要合并的Rande
auto beforeIt = it++;
while (it != myList.end() && beforeIt->second >= it->first) {
beforeIt->second = max(it->second, beforeIt->second);
it = myList.erase(it);
}
}
}
};