题解:洛谷 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

【核心思想】

  1. 问题分析:给定两个与坐标轴平行的矩形(割草机广告牌和牛饲料广告牌),求割草机广告牌未被牛饲料广告牌覆盖部分的面积,即需要用防水布覆盖的最小面积。这是一个矩形面积交/网格离散化问题,关键在于处理两个矩形的重叠关系,计算被遮挡后剩余的暴露面积。

  2. 算法选择

    • 坐标离散化 + 网格划分:收集两个矩形的所有 \(x\)\(y\) 坐标,排序后形成最多 \(3 \times 3\) 的网格单元
    • 逐格判断:对每个网格单元,判断其是否完全在割草机矩形内但不在牛饲料矩形内,累加这些单元的面积
  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\)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(1)\),固定 \(3 \times 3\) 网格扫描
    • 空间复杂度:\(O(1)\),仅需存储坐标和网格信息
  5. 坐标离散化的核心思想

    • 事件点分割:两个矩形的边界将平面划分为若干子区域,每个子区域要么完全在重叠区内、要么完全在外,无需处理部分覆盖的复杂几何
    • 降维处理:将二维矩形相交问题转化为离散的网格单元包含判断,避免浮点运算和复杂的几何公式
    • 完全包含特判:处理边界情况,确保结果正确性
    • 适用于"轴对齐矩形面积计算"类问题,核心是用坐标排序将连续平面离散化

【解题思路】

【算法标签】

普及- #模拟

【代码详解】

#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
posted @ 2026-08-28 15:54  团爸讲算法  阅读(2)  评论(0)    收藏  举报