ACWING\2032. 过度种植 (容斥原理与扫描线)

ACWING\2032. 过度种植 (容斥原理与扫描线)_第1张图片

输入样例:

2
0 5 4 1
2 4 6 2

输出样例:

20

题解

题目解读:

注意题目中给出 “轴向对齐”(即具有垂直和水平边)的矩形 的设定,若把每个给出的矩形区域面积计算,则交集出x、y方向矩形为重复计算的面积,这里即计算多个矩形重合后的尽占地面积。

方法一:扫描线 区间求交

如下图,以x方向将每个出现x坐标平行于y轴画一条线,若有n个矩形,则出现2n条线,将整个草地划分为2n-1个区域。每个区域内若存在种草的矩形,则一定是x方向的长度相同,只在y方向上考虑是否出现区间交错。

ACWING\2032. 过度种植 (容斥原理与扫描线)_第2张图片

首先处理x方向的画扫描线,需要将所有矩形按照x轴排序一次,将每个矩形的上下y轴坐标点添加到所属的扫描区域。

接下来处理每个扫描区域。由于x方向的长度相同,只需要确定y方向的实际长度即可。将y方向的线段进行排序,然后从小到达依次遍历得到实际y方向长度,获得占地面积。

将每个区域的面积相加即可。

方法二:容斥原理

由于本题限制了N的大小,所以可以使用容斥原理直接暴力计算面积大小。

对于多个集合求并集的公式如下,可以转化为交集计算:

∣ ⋃ i = 1 n A i ∣ = ∑ i = 1 n ∣ A i ∣ − ∑ i , j : 1 ≤ i < j ≤ n ∣ A i ∩ A j ∣ + ∑ i , j , k : 1 ≤ i < j < k ≤ n ∣ A i ∩ A j ∩ A k ∣ − … + ( − 1 ) n − 1 ∣ A 1 ∩ … ∩ A n ∣ \left|\bigcup_{i=1}^{n} A_{i}\right|=\sum_{i=1}^{n}\left|A_{i}\right|-\sum_{i, j: 1 \leq ii=1nAi=i=1nAii,j:1i<jnAiAj+i,j,k:1i<j<knAiAjAk+(1)n1A1An

公式中即对从1到n个集合进行求交集,再转化为整体的并集。

交集计算非常简单,对于两个规整的x y轴方向平行矩形,在笛卡尔坐标系下,可以有如下公式:

  • 左上坐标x1 = max(Rec1.x1,Rec2.x1)
  • 左上坐标y1 = min(Rec1.y1,Rec2.y1)
  • 右下坐标x2 = min(Rec1.x2,Rec2.x2)
  • 右下坐标y2 = max(Rec1.y2,Rec2.y2)

则将如上公式带入计算就可以暴力得到面积。

代码逻辑如下:

  • 利用二进制遍历每一种 矩形组合的情况,n个矩形,刚好有2^n种,即遍历1到 1>>n,每个数字的二进制就代表其矩形求交集的情况。如5的二进制为101表示第0和第3的矩形求交集。
  • 将每个组合状态的矩形按照如上交集公式遍历求得即可,若矩形数量为偶数,则需要减,奇数为加。
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

const int N = 20, INF = 1e5;

struct Rect{
        int x1,y1,x2,y2; // 左上 右下坐标
}rect[N];

int n;

int get(int state){
        // state表示存在的矩形
        int x1 = -INF,y1 = INF,x2 = INF,y2 = -INF; // 交集矩形的坐标
        int cnt = 0; // 计算矩阵个数
        for(int i = 0;i<n;i++){
                if(state>>i&1){
                        // 第i个矩形加入计算
                        cnt++;
                        x1 = max(x1,rect[i].x1); 
                        x2 = min(x2,rect[i].x2);
                        y1 = min(y1,rect[i].y1);
                        y2 = max(y2,rect[i].y2);                        
                }
        }
        int area = max(x2-x1,0) * max(y1-y2,0);
        area = cnt%2? area:-area;
        return area;
}

int main(){
        cin>>n;
        for(int i = 0;i<n;i++){
                int x1,y1,x2,y2;
                cin>>x1>>y1>>x2>>y2;
                rect[i] = {x1,y1,x2,y2};
        }
        int ans = 0;
        for(int i = 1;i< (1<<n); i++){
                // 二进制代表每一个位置,从0到n-1位置代表一个矩形是否加入计算
                ans += get(i);
        }
        
        cout<<ans<<endl;
        
        return 0;
}

你可能感兴趣的:(刷题,算法,c++,图论)