题解:洛谷 P1696 Blocked Billboard II
【题目来源】
洛谷:P1696 [USACO18JAN] Blocked Billboard II B - 洛谷(luogu.com.cn)
【题目描述】
奶牛 Bessie 曾经在她的谷仓里拥有如此美好的视野,可以看到马路对面两块广告牌,广告牌上宣传着看起来美味的牛饲料。不幸的是,其中一个广告牌最近更新了,现在宣传的是「农夫 Larry 的割草机」。Bessie 对割草机并不感兴趣,因为据她所知,割草机的唯一用途就是割掉她田里那些她觉得美味的草(如果你还没有注意到,Bessie 的思维过程大多围绕着食物)。
幸运的是,剩下的牛饲料广告牌位于割草机广告牌的前面,可能会遮挡它。
Bessie 决心要完全从她的视野中移除这个令人讨厌的割草机广告牌,于是她想出了一个冒险的计划。她计划从谷仓里偷一块大矩形防水布,然后在深夜偷偷溜出去,盖住割草机广告牌的剩余部分,这样她就再也看不到它的任何部分了。
给定两个广告牌的位置,请帮助 Bessie 计算她需要的最小防水布面积。由于谷仓里只有矩形大小的防水布,Bessie 观察到她可能需要一块面积稍大于割草机广告牌暴露面积的防水布,如下例所示。防水布只能放置在其边与其他广告牌的边平行的位置(即不能「倾斜」)。
【输入】
输入的第一行包含四个用空格分隔的整数:\(x_1, y_1, x_2, y_2\),其中 \((x_1, y_1)\) 和 \((x_2, y_2)\) 是 Bessie 的二维视野中割草机广告牌的左下角和右上角的坐标。下一行包含四个整数,同样指定牛饲料广告牌的左下角和右上角的坐标。牛饲料广告牌可能遮挡割草机广告牌的全部、部分或没有。所有坐标的范围是 \(-1000\) 到 \(+1000\)。
【输出】
请输出 Bessie 需要使用的防水布的最小面积,以覆盖割草机广告牌的部分,使其完全被遮挡。
【输入样例】
2 1 7 4
5 -1 10 3
【输出样例】
15
【核心思想】
-
问题分析:给定两个与坐标轴平行的矩形(割草机广告牌和牛饲料广告牌),求割草机广告牌未被牛饲料广告牌覆盖部分的面积,即需要用防水布覆盖的最小面积。这是一个矩形面积交/网格离散化问题,关键在于处理两个矩形的重叠关系,计算被遮挡后剩余的暴露面积。
-
算法选择:
- 坐标离散化 + 网格划分:收集两个矩形的所有 \(x\) 和 \(y\) 坐标,排序后形成最多 \(3 \times 3\) 的网格单元
- 逐格判断:对每个网格单元,判断其是否完全在割草机矩形内但不在牛饲料矩形内,累加这些单元的面积
-
关键步骤:
- 读取输入:割草机矩形坐标 \((x_1, y_1, x_2, y_2)\),牛饲料矩形坐标 \((x_3, y_3, x_4, y_4)\)
- 坐标收集:\(x = \{x_1, x_2, x_3, x_4\}\),\(y = \{y_1, y_2, y_3, y_4\}\)
- 排序离散化:
sort(x),sort(y),形成网格边界 - 完全包含特判:若一个矩形完全包含另一个,直接输出被包含矩形的面积
- 逐格扫描(\(i\) 从 \(1\) 到 \(3\),\(j\) 从 \(1\) 到 \(3\)):
- 网格单元 \([x_i, x_{i+1}] \times [y_j, y_{j+1}]\)
- 若
in(i, j, a)(在割草机内)且!in(i, j, b)(不在牛饲料内):\(ans \mathrel{+}= (x_{i+1} - x_i) \times (y_{j+1} - y_j)\)
- 输出 \(ans\)
-
时间/空间复杂度:
- 时间复杂度:\(O(1)\),固定 \(3 \times 3\) 网格扫描
- 空间复杂度:\(O(1)\),仅需存储坐标和网格信息
-
坐标离散化的核心思想:
- 事件点分割:两个矩形的边界将平面划分为若干子区域,每个子区域要么完全在重叠区内、要么完全在外,无需处理部分覆盖的复杂几何
- 降维处理:将二维矩形相交问题转化为离散的网格单元包含判断,避免浮点运算和复杂的几何公式
- 完全包含特判:处理边界情况,确保结果正确性
- 适用于"轴对齐矩形面积计算"类问题,核心是用坐标排序将连续平面离散化
【解题思路】

【算法标签】
普及- #模拟
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int a[5], b[5]; // 存储两个矩形的坐标:a[广告牌], b[覆盖物]
int ans = 0, ans2 = 0; // ans: 未被覆盖的面积, ans2: 被覆盖的面积
int x[5], y[5]; // 存储所有x坐标和y坐标(用于网格划分)
// 矩形的四个顶点坐标
int x11, x12, x21, x22; // 广告牌和覆盖物的x坐标
int y11, y12, y21, y22; // 广告牌和覆盖物的y坐标
ifstream filein("billboard.in");
ofstream fileout("billboard.out");
/**
* 判断网格单元是否在矩形内部
* @param i x方向网格索引
* @param j y方向网格索引
* @param k 矩形的坐标数组
* @return 如果网格单元完全在矩形内返回true,否则false
*/
bool in(int i, int j, int k[])
{
// 检查网格单元是否完全包含在矩形内
if (x[i] >= k[1] && x[i + 1] <= k[3] &&
y[j] >= k[2] && y[j + 1] <= k[4])
{
return true;
}
return false;
}
int main()
{
// 输入广告牌的坐标(左下角和右上角)
for (int i = 1; i <= 4; i++)
{
filein >> a[i];
}
x11 = a[1]; // 广告牌左下角x
x12 = a[3]; // 广告牌右上角x
y11 = a[2]; // 广告牌左下角y
y12 = a[4]; // 广告牌右上角y
// 输入覆盖物的坐标(左下角和右上角)
for (int i = 1; i <= 4; i++)
{
filein >> b[i];
}
x21 = b[1]; // 覆盖物左下角x
x22 = b[3]; // 覆盖物右上角x
y21 = b[2]; // 覆盖物左下角y
y22 = b[4]; // 覆盖物右上角y
// 收集所有x坐标和y坐标
x[1] = a[1]; // 广告牌左边界
x[2] = a[3]; // 广告牌右边界
x[3] = b[1]; // 覆盖物左边界
x[4] = b[3]; // 覆盖物右边界
y[1] = a[2]; // 广告牌下边界
y[2] = a[4]; // 广告牌上边界
y[3] = b[2]; // 覆盖物下边界
y[4] = b[4]; // 覆盖物上边界
// 对坐标进行排序,用于网格划分
sort(x + 1, x + 4 + 1);
sort(y + 1, y + 4 + 1);
// 特殊情况处理:覆盖物完全包含广告牌或广告牌完全包含覆盖物
if ((y22 >= y12 && y11 >= y21 && x12 >= x22 && x21 >= x11) ||
(y12 >= y22 && y21 >= y11 && x22 >= x12 && x11 >= x21))
{
// 输出广告牌的面积(完全被覆盖或完全覆盖)
fileout << (y12 - y11) * (x12 - x11) << endl;
return 0;
}
// 遍历所有网格单元(3x3网格)
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 3; j++)
{
// 检查网格单元是否在广告牌内但不在覆盖物内
if (in(i, j, a) && !in(i, j, b))
{
// 计算未被覆盖的网格单元面积并累加
ans += (y[j + 1] - y[j]) * (x[i + 1] - x[i]);
}
// 检查网格单元是否同时在广告牌和覆盖物内
if (in(i, j, a) && in(i, j, b))
{
// 计算被覆盖的网格单元面积并累加
ans2 += (y[j + 1] - y[j]) * (x[i + 1] - x[i]);
}
}
}
// 特殊情况:部分覆盖且覆盖区域不规则
if (ans2 % (y12 - y11) != 0 && ans2 % (x12 - x11) != 0)
{
// 使用整个广告牌的面积作为结果
ans = (y12 - y11) * (x12 - x11);
}
// 输出未被覆盖的面积
fileout << ans << endl;
return 0;
}
【运行结果】
2 1 7 4
5 -1 10 3
15
浙公网安备 33010602011771号