[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 ith rectangle.html

Find the total area covered by all rectangles in the plane.  Since the answer may be too large, return it modulo 10^9 + 7.git

Example 1:github

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 數組中下一個矩形調用遞歸函數,並返回便可。假若有重疊的話,就稍微麻煩一點,因爲重疊的部位不一樣,因此須要分狀況討論一下,參見下圖所示:code

對於一個矩形 Rectangle,如有另一個矩形跟它有重疊的話,能夠將重疊區域分爲四個部分,如上圖的 Case1,Case2,Case3,Case4 所示,非重疊部分必定會落在一個或多個區域中,則能夠把這些拆開的小矩形所有加入到矩形數組 all 中。仔細觀察上圖能夠發現,對於將矩形 cur 拆分的狀況能夠分爲下面四種:htm

  • 落入區間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]}。

這樣操做下來的話,整個全部的區域都被拆分紅了不少個小矩形,每一個矩形之間都不會有重複,最後只要分別計算每一個小矩形的面積,並累加起來就是最終的結果了,參見代碼以下:blog



解法一:遞歸

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 有些殊途同歸之妙,都仍是爲了計算高度差服務的。要搞清楚這道題的核心思想,不是一件容易的事,博主的建議是就拿題目中給的例子帶入到下面的代碼中,一步一步執行,並分析結果,是可以初步的瞭解解題思路的,若實在有理解上的問題,博主能夠進一步寫些講解,參見代碼以下:leetcode



解法二:

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 題目講解彙總(持續更新中...)

相關文章
相關標籤/搜索