📌  相关文章
📜  找到一个插入另一个矩形后剩余的最小矩形数(1)

📅  最后修改于: 2023-12-03 15:39:45.319000             🧑  作者: Mango

题目介绍

有两个矩形 $A$ 和 $B$,它们的边平行于坐标轴。现在你需要把矩形 $B$ 插入到矩形 $A$ 中,使得矩形 $B$ 和矩形 $A$ 不重叠,但是矩形 $B$ 与矩形 $A$ 相切也是允许的。最后,求出插入矩形 $B$ 后,剩余的最小矩形数。

解题思路

首先判断矩形 $B$ 是否能够插入矩形 $A$ 中,即矩形 $B$ 的四个顶点是否都在矩形 $A$ 内部。如果不能插入,则矩形 $B$ 插入后剩余的最小矩形数就是 $1$,即矩形 $A$。

如果矩形 $B$ 能够插入矩形 $A$ 中,我们可以把矩形 $A$ 按照矩形 $B$ 的位置分割成若干个矩形,然后计算出剩余的矩形个数即可。分割的方法可以采用线段树或者扫描线。

代码实现

下面是采用线段树的代码实现。首先定义一个结构体 Seg,用来存储线段树的一些信息。然后定义一个 build 函数进行线段树的初始化,具体来说就是将初始的矩形 $A$ 加入到线段树中。然后定义一个 insert 函数,用来插入矩形 $B$,具体来说就是将矩形 $B$ 和线段树中相交的矩形合并,然后递归更新线段树即可。最后定义一个 query 函数,用来计算剩余的矩形个数。

struct Seg {
    int l, r;
    mutable int v;
    bool operator<(const Seg& other) const {
        return l < other.l;
    }
};

set<Seg> segs;

set<Seg>::iterator split(int pos) {
    auto it = segs.lower_bound({pos, 0});
    if (it != segs.end() && it->l == pos) {
        return it;
    }
    --it;
    int l = it->l, r = it->r, v = it->v;
    segs.erase(it);
    segs.insert({l, pos - 1, v});
    return segs.insert({pos, r, v}).first;
}

void assign(set<Seg>::iterator l, set<Seg>::iterator r, int value) {
    auto it = l;
    while (it != r) {
        auto nx = next(it);
        segs.erase(it);
        segs.insert({it->l, it->r, value});
        it = nx;
    }
}

void build(int l, int r, int v) {
    segs.insert({l, r, v});
}

void insert(int l, int r, int v) {
    auto itr = split(l);
    auto itl = split(r + 1);
    assign(itr, itl, v);
}

int query() {
    int res = 0;
    auto it = segs.begin();
    while (it != segs.end()) {
        res += (it->v != 0);
        int value = it->v;
        while (it != segs.end() && it->v == value) {
            ++it;
        }
    }
    return res;
}

下面是主函数,具体流程就是输入两个矩形的坐标,判断能否插入到一起,若不行则直接输出 $1$,否则按照线段树的方法计算剩余矩形个数。

int main() {
    int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2;
    scanf("%d%d%d%d%d%d%d%d", &ax1, &ay1, &ax2, &ay2, &bx1, &by1, &bx2, &by2);

    if (bx1 >= ax2 || ay2 <= by1 || bx2 <= ax1 || by2 >= ay1) {
        printf("1\n");
        return 0;
    }

    build(ax1, ax2, 1);
    build(bx1, bx2, 0);
    build(ax1, ax2, 1);
    build(bx1, bx2, 0);
    insert(ax1, ax2, 2);
    insert(bx1, bx2, 1);
    insert(ax1, ax2, 2);
    insert(bx1, bx2, 1);

    printf("%d\n", query());
    return 0;
}
总结

本题涉及到的算法包括线段树和扫描线,比较综合和复杂,但是思路还是比较清晰的。需要考虑细节和边界条件,否则很容易写出错误的代码。