欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

[LeeCode 391. 完美矩形]找规律+线段树扫描线

程序员文章站 2022-03-21 07:50:18
...

[LeeCode 391. 完美矩形]找规律+线段树扫描线

1. 题目链接

[LeeCode 391. 完美矩形]

2. 题意描述

[LeeCode 391. 完美矩形]找规律+线段树扫描线

3. 解题思路

首先,完美矩形是一种精确覆盖.
有下面两条性质:

  1. 小矩形的面积之和等于大矩形的面积;
  2. 矩形不能重叠,也就是矩形最大覆盖数为1.

而且,很容易证明,满足上述两条性质,就一定是完美矩形.
对于第一条性质,只需要求出大矩形的边界就好了;
对于第二条性质,是一个线段树扫描线求矩形最大覆盖数的经典题. 具体做法,将横坐标离散化,然后将矩形拆分成上下两条线段, 将线段按照高度从小到大排序, 依次添加到线段树中(线段树保存的各个坐标当前覆盖的次数,下边界区间加法, 上边界区间减法).然后就可以统计出矩形的最大覆盖数了.

4. 参考代码

#ifdef __LOCAL_WONZY__
#include <bits/stdc++.h>
using namespace std;
#endif

class Solution {
public:
    typedef long long ll;
    static const int inf = 0x3f3f3f3f;
    struct Line {
        int le, ri, h, t;
        Line() {}
        Line(int le, int ri, int h, int t) : le(le), ri(ri), h(h), t(t) {}
        bool operator < (const Line& e) const {
            if(h == e.h) return t < e.t;
            return h < e.h;
        }
    };
    vector<int> fr, fc;
    vector<int> seg, tag;

    int rhash(int x) {
        return lower_bound(fr.begin(), fr.end(), x) - fr.begin();
    }
    int chash(int x) {
        return lower_bound(fc.begin(), fc.end(), x) - fc.begin();
    }
    void pushDw(int rt, int w) {
        if(!tag[rt]) {
            seg[rt << 1] += tag[rt];
            seg[rt << 1 | 1] += tag[rt];
            tag[rt << 1] += tag[rt];
            tag[rt << 1 | 1] += tag[rt];
            tag[rt] = 0;
        }
    }
    void pushUp(int rt) {
        seg[rt] = max(seg[rt << 1], seg[rt << 1 | 1]);
    }
    void update(const int L, const int R, const int val, int le, int ri, int rt) {
        if(L <= le && ri <= R) {
            seg[rt] += val;
            tag[rt] += val;
            return;
        }
        pushDw(rt, ri - le + 1);
        int md = (le + ri) >> 1;
        if(L <= md) update(L, R, val, le, md, rt << 1);
        if(R > md) update(L, R, val, md + 1, ri, rt << 1 | 1);
        pushUp(rt);
    }
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        vector<Line> lines;
        long long area = 0;
        vector<int> d = {inf, inf, -inf, -inf};
        for(auto& e : rectangles) {
            fr.push_back(e[0]); //fc.push_back(e[1]);
            fr.push_back(e[2]); //fc.push_back(e[3]);
            fr.push_back(e[0] - 1);
            fr.push_back(e[2] - 1);
            d[0] = min(d[0], e[0]); d[1] = min(d[1], e[1]);
            d[2] = max(d[2], e[2]); d[3] = max(d[3], e[3]);
            lines.push_back(Line(e[0], e[2], e[1], +1));
            lines.push_back(Line(e[0], e[2], e[3], -1));
            area += (e[2] - e[0]) * (e[3] - e[1]);
        }
        if(area != (long long)(d[2] - d[0]) * (d[3] - d[1])) return false;
        sort(fr.begin(), fr.end());
        //sort(fc.begin(), fc.end());
        fr.erase(unique(fr.begin(), fr.end()), fr.end());
        //fc.erase(unique(fc.begin(), fc.end()), fc.end());
        sort(lines.begin(), lines.end());
        int m = fr.size();
        seg = tag = vector<int>(m << 2, 0);
        int mval = 0;
        for(auto& e : lines) {
            int lpos = rhash(e.le), rpos = rhash(e.ri - 1);
            update(lpos, rpos, e.t, 0, m - 1, 1);
            mval = max(mval, seg[1]);
        }
        return mval <= 1;
    }
};

#ifdef __LOCAL_WONZY__
int main() {
#ifdef __LOCAL_WONZY__
    freopen("input-1.txt", "r", stdin);
#endif
    vector<vector<int>> a = {
        {1,1,3,3},
        {3,1,4,2},
        {3,2,4,4},
        {1,3,2,4},
        {2,3,3,4},
    };
    vector<vector<int>> b = {
        {1,1,2,3},
        {1,3,2,4},
        {3,1,4,2},
        {3,2,4,4},
    };
    vector<vector<int>> c = {
        {1,1,3,3},
        {3,1,4,2},
        {1,3,2,4},
        {2,2,4,4},
    };
    Solution s;
    cout << s.isRectangleCover(c) << endl;;
    return 0;
}

#endif
相关标签: 线段树