算法题(过度种植)

216 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第21天,点击查看活动详情

题目信息

农夫约翰购买了一台新机器,该机器能够在其农场的任何“轴向对齐”(即具有垂直和水平边)的矩形区域内种草。

不幸的是,这台机器有一天出了故障,并在 N 个不同的矩形区域内进行了种草工作,其中一些区域可能会有重叠。

给定机器工作的具体 N 个矩形区域,请你计算种上草的区域的总面积是多少。

输入格式

第一行包含整数 N。

接下来 N 行,每行包含四个整数 x1,y1,x2,y2,表示其中一个矩形区域的左上角坐标 (x1,y1) 和右下角坐标 (x2,y2)。

输出格式

输出种上草的区域的总面积。

数据范围

1≤N≤10, −10000≤x1,y1,x2,y2≤10000, x1<x2, y1>y2。

输入样例:

2
0 5 4 1
2 4 6 2

输出样例:

20

思路

区间覆盖问题,这题是一个二维的,分别对每一行做区间覆盖,按左端点排序,如果右端点小于前一个区间的右端点,直接continue,如果左端点小于前一个区间的右端点且右端点大于前一个区间的右端点,直接更新右端点,如果左端点大于前一个区间的右端点,求出上一个区间覆盖的距离,在更新左端点和右端点。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
​
typedef pair<int, int> PII;
typedef long long LL;
const int N = 20010;
vector<PII> v[N];
int n;
​
int main()
{
    int x1, y1, x2, y2;
    cin >> n;
    int mi = 20000, mx = 0;
    for(int i = 0; i < n; i ++ )
    {
        cin >> x1 >> y1 >> x2 >> y2;
        x1 += 10000, x2 += 10000;//下标不能是负值所以我们把负值变成正值
        mi = min(mi, x1), mx = max(mx, x2 - 1);
        for(int j = x1; j < x2; j ++ )
            v[j].push_back({y2, y1});
    }
​
    LL res = 0;
    for(int i = mi; i <= mx; i ++ )
    {
        if(!v[i].empty())
        {//这里判断一下,否则会段错误
            sort(v[i].begin(), v[i].end());
            int lt = v[i][0].first, rt = v[i][0].second;
            for(int j = 1; j < v[i].size(); j ++ )
            {
                int a = v[i][j].first, b = v[i][j].second;
                if(b <= rt) continue;
                else if(a > rt)
                {
                    res += rt - lt;
                    lt = a, rt = b;
                }
                else if(b > rt) rt = b;
            }
            res += rt - lt;//最后不要忘记加上最后一个区间
        }
    }
​
    cout << res << endl;
​
    return 0;
}