题解:洛谷 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
【核心思想】
-
问题分析:给定 \(N\) 个矩形(左上角 \((x_1,y_1)\),右下角 \((x_2,y_2)\)),求它们并集的面积。坐标范围达 \(10^8\),但 \(N \leq 1000\)。本质上是坐标离散化 + 网格标记问题:将大坐标压缩到小范围,用布尔矩阵标记覆盖区域后累加实际面积。
-
算法选择:
- 坐标离散化:将所有矩形的 \(x\) 坐标和 \(y\) 坐标分别排序去重,映射到连续整数
- 网格标记:在离散化后的网格上标记每个小格子是否被覆盖
- 面积累加:对每个被覆盖的小格子,计算其实际面积(相邻离散坐标的差值乘积)
-
关键步骤:
- 读取数据:读入 \(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\)
-
时间/空间复杂度:
- 时间复杂度:\(O(N^3)\),离散化 \(O(N \log N)\),标记 \(O(N^2)\),面积计算 \(O(N^2)\)
- 空间复杂度:\(O(N^2)\),离散坐标数组和布尔标记矩阵
-
离散化 + 网格标记的核心思想:
- 坐标压缩:\(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
浙公网安备 33010602011771号