[LeetCode] 850. Rectangle Area II 矩形面積之二

2021-02-21 刷盡天下

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 ith 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 的拓展,那道題只有兩個矩形重疊,而這道題有多個矩形可能同時重疊,整體難度一下就上來了,那麼通過將所有矩形面積加起來再減去重疊區域的方法這裡就不太適用了,因為多個矩形在同一區域重疊的話,都減去重疊面積是會錯的,還得把多減的補回來,相當的麻煩。這裡我們需要換一種解題的思路,不能一股腦兒的把所有的矩形都加起來,而是應該利用微積分的思想,將重疊在一起的區域拆分成一個個的小矩形,分別累加面積,因為這裡的矩形是不會旋轉的,所以是可以正常拆分的。思路有了,新建一個二維數組 all 來保存所有的矩形,然後遍歷給定的矩形數組,對於每個遍歷到的數組,調用一個子函數,將當前的矩形加入 all 中。下面主要來看一下這個子函數 helper 該如何實現?首先要明白這個函數的作用是將當前矩形加入 all 數組中,而且用的是遞歸的思路,所以要傳入一個 start 變量,表示當前和 all 數組中正在比較的矩形的 index,這樣在開始的時候,檢查一下若 start 大於等於 all 數組長度,表示已經檢測完 all 中所有的矩形了,將當前矩形加入 all 數組,並返回即可。否則的話則取出 start 位置上的矩形 rec,此時就要判斷當前要加入的矩形和這個 rec 矩形是否有重疊,這在 LeetCode 中有專門一道題是考察這個的 Rectangle Overlap,這裡用的就是那道題的判斷方法,假如判斷出當前矩形 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 有些異曲同工之妙,都還是為了計算高度差服務的。要搞清楚這道題的核心思想,不是一件容易的事,博主的建議是就拿題目中給的例子帶入到下面的代碼中,一步一步執行,並分析結果,是能夠初步的了解解題思路的,若實在有理解上的問題,博主可以進一步寫些講解,參見代碼如下:


解法二:

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


類似題目:

Rectangle Overlap

Rectangle Area

The Skyline Problem


參考資料:

https://leetcode.com/problems/rectangle-area-ii/

https://leetcode.com/problems/rectangle-area-ii/discuss/138028/Clean-Recursive-Solution-Java

https://leetcode.com/problems/rectangle-area-ii/discuss/214365/Short-C%2B%2B-solution.-EZ-to-understand.-Beat-99.


LeetCode All in One 題目講解匯總(持續更新中...)

相關焦點

  • leetcode - 可以形成最大正方形的矩形數目
    題意給你一個數組 rectangles ,其中 rectangles[i] = [li, wi] 表示第 i 個矩形的長度為 li、寬度為 wi。如果存在 k同時滿足 k <= li 和 k <= wi,就可以將第 i個矩形切成邊長為 k的正方形。
  • 九年級數學,二次函數中矩形周長、面積最值問題,解題方法不同
    二次函數中矩形周長的最值問題與面積的最值問題,思考方法不一樣。矩形周長的最值問題一般藉助設點法表示出矩形的長和寬,然後利用公式得到周長,一般化簡後為二次函數,然後通過研究二次函數的性質得到最值。矩形面積最值問題,考查比較多的為籬笆問題,以實際應用題居多,籬笆問題中有一類題目需要特別注意,那就是含有「門」的籬笆問題,在處理時不要忽略「門」的存在。
  • ​LeetCode刷題實戰137:只出現一次的數字 II
    今天和大家聊的問題叫做只出現一次的數字 II,我們先來看題面:https://leetcode-cn.com/problems/single-number-ii/Given an integer array nums where every element appears three times except for one, which appears exactly
  • [LeetCode] 976. Largest Perimeter Triangle 最大周長的三角形
    Given an array A of positive lengths, return the largest perimeter of a triangle with non-zero area
  • leetcode鍊表之回文鍊表
    序本文主要記錄一下leetcode鍊表之回文鍊表題目
  • leetcode之羅馬數字轉整數
    序本文主要記錄一下leetcode之羅馬數字轉整數題目給定一個羅馬數字,將其轉換成整數。
  • LeetCode145|數組中數字出現的次數II
    .get() .getKey(); }}5,總結一下對於本題,整體最容易理解的思路就是基於鍵值對集合hashmap進行解決了歷史文章目錄數據結構:王同學下半年曾寫過的JDK集合源碼分析文章匯總算法匯總:leetcode
  • 使用Python OpenCV處理圖像之詳解直線、圓、矩形及文字的繪製
    Python使用tkinter製作一個簡易的繪圖程序一(Python GUI編程))中講到的繪製基本的直線、圓、橢圓、矩形、多邊形、文字等。我們要開始了【內容】直線、圓、橢圓、矩形、多邊形、文字的繪製方法通過基礎知識的學習,我們繪製一個OpenCV的logo圖像並顯示保存【基本圖形繪製】
  • 面積計算(二)
    為什麼說三角形的面積公式是最基本的?因為我們可以用三角形來生成所有的多邊形。換句話說,只要有三角形的面積公式,我們就可以計算出一切多邊形的面積,當然,僅僅是理論上。 首先我們來看平行四邊形的面積。平行四邊形是指兩組對邊都平行的四邊形,我們把兩個相對的頂點連起來,就得到了平行四邊形的一條對角線。
  • 矩形集結(一) 之PISA題(配練習)
    一是基本元素小矩形,另外兩個陰影矩形,再一起圍成一個大矩形。我們可以設小矩形的兩邊長為a,b。接下來,把陰影矩形的邊長用字母表示出來,再觀察各邊長特點,再巧妙求解。具體解題過程如下:2、(2013年浙江寧波卷第12題)7張如圖2(1)的長為a,寬為b(a>b)的小長方形紙片,按圖2(2)的方式不重疊地放在矩形ABCD內,未被覆蓋的部分(兩個矩形)用陰影表示,設左上角與右下角的陰影部分的面積的差為
  • LeetCode面試系列 第6天:No.9 - 迴文數
    迴文數https://leetcode-cn.com/problems/palindrome-number/題目描述判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數。
  • 簡單VB小程序|手把手教你做,矩形面積計算器
    今天教大家怎麼使用VB來製作一款簡單的小程序,矩形面積計算器。首先在新建工程界面選擇標準EXE後點擊打開按鈕。然後將Form1屬性窗口中的Caption值設置為:矩形面積計算器。在對象窗口中可以調節計算器界面的大小。在工具箱中選擇Label工具,並在窗體設計器窗口中創建三個Label。
  • 每日一道 LeetCode (15):二進位求和
    LeetCode 前文合集代碼倉庫GitHub:https://github.com/meteor1993/LeetCodeGitee:https://gitee.com/inwsy/LeetCode題目:數組加一題目來源:https://leetcode-cn.com
  • [LeetCode] 912. Sort an Array 數組排序
    但是此時左右兩邊的數組各自都不一定是有序的,需要再各自調用相同的遞歸,直到細分到只有1個數字的時候,再返回的時候就都是有序的了,參見代碼如下:解法二:class Solution {public: vector<int> sortArray(vector<int>& nums) {
  • LeetCode-89.格雷編碼(Gray Code)
    格雷編碼格雷編碼是一個二進位數字系統,在該系統中,兩個連續的數值僅有一個位數的差異。給定一個代表編碼總位數的非負整數 n,列印其格雷編碼序列。即使有多個不同答案,你也只需要返回其中一種。格雷編碼序列必須以 0 開頭。
  • LeetCode刷題第三周【數組(簡單)】
    4.1 解題思路圖中是一個F(5)計算的遞歸樹(圖片來自leetcode官方[7])。在這裡我們需要在初始化的時候讓第一二兩行值都為1,從第三行開始計算。參考資料[1]Leetcode網站: https://leetcode-cn.com/[2]以上文字描述均來自 LeetCode數組模塊: https://leetcode-cn.com/tag/array/[3]刷題請點擊或見附錄:1539.
  • leetcode刷對了麼
    今天,逆行君就帶你從世界觀和方法論兩方面走進「leetcode」 簡單來說,leetcode是一個美國的在線編程網站,它收集了各大公司的經典算法面試題,用戶可以選擇不同的語言進行代碼的在線編寫、編譯和調試。