题解:洛谷 P1884 Overplanting

【题目来源】

洛谷:P1884 [USACO12FEB] Overplanting S - 洛谷

【题目描述】

在一个笛卡尔平面坐标系里(则 \(X\) 轴向右是正方向,\(Y\) 轴向上是正方向),有 \(N(1\le N\le 1000)\) 个矩形,第 \(i\) 个矩形的左上角坐标是 \((x_1,y_1)\),右下角坐标是 \((x_2,y_2)\)。问这 \(N\) 个矩形所覆盖的面积是多少?

注意:被重复覆盖的区域的面积只算一次。

【输入】

第一行,一个整数 \(N(1\le N\le 1000)\)

接下来有 \(N\) 行,每行描述一个矩形的信息,分别是矩形的 \(x_1,y_1,x_2,y_2(-10^8\le x_1,y_1,x_2,y_2,\le 10^8)\)

【输出】

一个整数,被 \(N\) 个矩形覆盖的区域的面积。

【输入样例】

2
0 5 4 1
2 4 6 2

【输出样例】

20

【核心思想】

  1. 问题分析:给定 \(N\) 个矩形(左上角 \((x_1,y_1)\),右下角 \((x_2,y_2)\)),求它们并集的面积。坐标范围达 \(10^8\),但 \(N \leq 1000\)。本质上是坐标离散化 + 网格标记问题:将大坐标压缩到小范围,用布尔矩阵标记覆盖区域后累加实际面积。

  2. 算法选择

    • 坐标离散化:将所有矩形的 \(x\) 坐标和 \(y\) 坐标分别排序去重,映射到连续整数
    • 网格标记:在离散化后的网格上标记每个小格子是否被覆盖
    • 面积累加:对每个被覆盖的小格子,计算其实际面积(相邻离散坐标的差值乘积)
  3. 关键步骤

    • 读取数据:读入 \(N\)\(N\) 个矩形坐标
    • 收集坐标:将所有 \(x_1, x_2\) 存入 \(a\) 数组,\(y_1, y_2\) 存入 \(b\) 数组
    • 离散化
      • sort + unique 去重,得到 lena 个不同 \(x\) 坐标和 lenb 个不同 \(y\) 坐标
    • 映射与标记
      • lower_bound 将每个矩形的坐标映射到离散索引
      • 遍历矩形覆盖的离散网格范围,c[j][k] = true
    • 面积计算
      • 遍历所有离散小格子,若被覆盖则累加 \((a[i+1]-a[i]) \times (b[j+1]-b[j])\)
    • 输出 \(ans\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(N^3)\),离散化 \(O(N \log N)\),标记 \(O(N^2)\),面积计算 \(O(N^2)\)
    • 空间复杂度:\(O(N^2)\),离散坐标数组和布尔标记矩阵
  5. 离散化 + 网格标记的核心思想

    • 坐标压缩\(10^8\) 范围无法直接开数组,但 \(N \leq 1000\) 意味着最多 \(2000\) 个不同坐标,离散化后网格大小可控
    • 小格子的实际面积:离散化后相邻坐标间的距离可能不等,必须用原始坐标差值计算实际面积,不能简单计数
    • 并集的去重:布尔标记 c[j][k] 保证同一小格子只被计算一次,自然实现并集面积
    • 输入顺序注意:题目输入顺序为 \(y_1, x_2, y_2, x_1\),代码中已做对应调整
    • 适用于大坐标范围、矩形并集、面积计算类问题

【解题思路】

【算法标签】

普及+ #离散化

【代码详解】

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

#define int long long  // 定义int为long long类型

// 矩形结构体,存储对角线坐标
struct Node
{
    int x1, y1, x2, y2;
} rect[1005];  // 存储所有矩形

int n, a[2005], b[2005], ans;  // n: 矩形数量,a/b: 坐标离散化数组,ans: 面积结果
bool c[2005][2005];  // 标记矩阵,记录每个小格子是否被覆盖

signed main()
{
    // 输入矩形数量
    cin >> n;

    // 输入每个矩形的坐标并存入离散化数组
    for (int i = 0; i < n; i++)
    {
        // 注意输入顺序是y1,x2,y2,x1
        cin >> rect[i].y1 >> rect[i].x2 >> rect[i].y2 >> rect[i].x1;
        a[i * 2] = rect[i].x1;
        a[i * 2 + 1] = rect[i].x2;
        b[i * 2] = rect[i].y1;
        b[i * 2 + 1] = rect[i].y2;
    }

    // 对x坐标进行离散化
    sort(a, a + 2 * n);
    int lena = unique(a, a + 2 * n) - a;  // 去重后的x坐标数量

    // 对y坐标进行离散化
    sort(b, b + 2 * n);
    int lenb = unique(b, b + 2 * n) - b;  // 去重后的y坐标数量

    // 将矩形坐标映射到离散化后的索引
    for (int i = 0; i < n; i++)
    {
        rect[i].x1 = lower_bound(a, a + lena, rect[i].x1) - a;
        rect[i].x2 = lower_bound(a, a + lena, rect[i].x2) - a;
        rect[i].y1 = lower_bound(b, b + lenb, rect[i].y1) - b;
        rect[i].y2 = lower_bound(b, b + lenb, rect[i].y2) - b;

        // 标记被当前矩形覆盖的小格子
        for (int j = rect[i].x1; j < rect[i].x2; j++)
        {
            for (int k = rect[i].y1; k < rect[i].y2; k++)
            {
                c[j][k] = 1;
            }
        }
    }

    // 计算总面积
    for (int i = 0; i < lena - 1; i++)
    {
        for (int j = 0; j < lenb - 1; j++)
        {
            if (c[i][j])
            {
                // 累加每个被覆盖小格子的实际面积
                ans += (a[i + 1] - a[i]) * (b[j + 1] - b[j]);
            }
        }
    }

    // 输出结果
    cout << ans;

    return 0;
}

【运行结果】

2
0 5 4 1
2 4 6 2
20
posted @ 2026-08-17 09:46  团爸讲算法  阅读(10)  评论(0)    收藏  举报