题解:洛谷 P1789 插火把

【题目来源】

洛谷:P1789 【Mc生存】插火把 - 洛谷

【题目描述】

话说有一天 linyorson 在“我的世界”开了一个 \(n\times n\) 的方阵,现在他有 \(m\) 个火把和 \(k\) 个萤石,分别放在 \((x_1,y_1)\sim (x_m,y_m)\)\((o_1,p_1)\sim (o_k,p_k)\) 的位置,没有光并且没放东西的地方会生成怪物。请问在这个方阵中有几个点会生成怪物?

P.S. 火把的照亮范围是:

    |暗|暗| 光 |暗|暗|
    |暗|光| 光 |光|暗|
    |光|光|火把|光|光|
    |暗|光| 光 |光|暗|
    |暗|暗| 光 |暗|暗|

萤石:

    |光|光| 光 |光|光|
    |光|光| 光 |光|光|
    |光|光|萤石|光|光|
    |光|光| 光 |光|光|
    |光|光| 光 |光|光|

【输入】

输入共 \(m+k+1\) 行。

第一行为 \(n,m,k\)

\(2\) 到第 \(m+1\) 行分别是火把的位置 \(x_i,y_i\)

\(m+2\) 到第 \(m+k+1\) 行分别是萤石的位置 \(o_i,p_i\)

注:可能没有萤石,但一定有火把。

【输出】

有几个点会生出怪物。

【输入样例】

5 1 0
3 3

【输出样例】

12

【核心思想】

  1. 问题分析:在一个 \(n \times n\) 的网格中,放置了若干火把和萤石。火把照亮一个十字形加四个对角格子的区域(共 13 格,但需考虑边界),萤石照亮一个 \(5 \times 5\) 的正方形区域(共 25 格,需考虑边界)。需要统计网格中没有被任何光源照亮的格子数量(这些格子会生成怪物)。本质是一个二维网格覆盖标记问题,可以使用布尔数组标记所有被照亮的格子,最后统计未标记的格子数。

  2. 算法选择

    • 标记数组模拟:使用一个二维布尔数组 light[1..n][1..n] 初始为 false,表示未被照亮。对于每个光源,根据其类型和位置,将对应照亮范围内的格子标记为 true。最后遍历整个网格,统计 false 的数量。
    • 边界处理:在标记时需检查坐标是否在 \([1, n]\) 范围内,避免数组越界。
  3. 关键步骤

    • 读入与初始化:读取 \(n, m, k\),将 light 数组全部置为 0(未照亮)。
    • 处理火把:对于每个火把位置 (x, y)
      • 标记十字形区域:(x±1, y)(x, y±1) 以及 (x±2, y)(x, y±2)(在边界内)。
      • 标记四个对角格子:(x-1, y-1)(x-1, y+1)(x+1, y-1)(x+1, y+1)
      • 注意:火把本身位置也照亮(但题目中可能隐含,代码中虽未显式标记火把自身,但十字形包括了自身?实际上代码中 a[x][y] 并未显式置 1,但火把自身位置应视为被照亮,代码中通过 a[x-1][y-1] 等不包含自身,这是一个潜在的问题,需要补充 a[x][y] = 1)。根据常见题意,火把所在格也应被照亮。
    • 处理萤石:对于每个萤石位置 (x, y),标记 \([x-2, x+2] \times [y-2, y+2]\) 范围内的所有格子(在边界内)。
    • 统计:遍历所有格子,统计 light[i][j] == 0 的数量并输出。
  4. 时间/空间复杂度

    • 时间复杂度:\(O(n^2 + (m+k) \cdot R)\),其中 \(R\) 是每个光源的照亮面积(常数),\(n \le 100\),完全可行。
    • 空间复杂度:\(O(n^2)\)
  5. 模拟与网格覆盖

    • 标记法的通用性:用数组模拟网格状态是处理此类“覆盖/照亮”问题的标准方法。
    • 边界检查:在标记过程中必须检查坐标是否在 \([1, n]\) 内,否则会导致数组越界。
    • 注意火把自身:火把所在的格子显然会被照亮,代码中应显式将 a[x][y] 设为 1。
    • 适用场景:这类问题常见于模拟题,核心是准确理解光源的照亮形状并将其转化为网格标记。

【算法标签】

入门 #模拟

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件(万能头文件)
using namespace std;      // 使用标准命名空间

int main()
{
    int n, m, k;                // 定义变量:网格大小n×n,m个路灯,k个临时照明
    int a[105][105] = {0};      // 定义二维数组:标记网格照明状态(0未照亮,1照亮)
    int x, y;                   // 定义变量:临时存储坐标
    int ans = 0;                // 定义变量:未被照亮的格子计数

    cin >> n >> m >> k;         // 输入网格大小、路灯数和临时照明数

    // 处理路灯照明范围
    for (int i = 0; i < m; i++)
    {
        cin >> x >> y;          // 输入路灯坐标

        // 照亮十字区域(上下左右各两格)
        for (int j = x - 2; j <= x + 2; j++)
            if (j >= 0) a[j][y] = 1;
        for (int j = y - 2; j <= y + 2; j++)
            if (j >= 0) a[x][j] = 1;

        // 照亮四个对角格子
        a[x - 1][y - 1] = 1;
        a[x + 1][y - 1] = 1;
        a[x - 1][y + 1] = 1;
        a[x + 1][y + 1] = 1;
    }

    // 处理临时照明范围
    for (int i = 0; i < k; i++)
    {
        cin >> x >> y;          // 输入临时照明坐标

        // 照亮5×5的正方形区域
        for (int p = x - 2; p <= x + 2; p++)
        {
            for (int q = y - 2; q <= y + 2; q++)
            {
                if (p >= 0 && q >= 0)
                    a[p][q] = 1;
            }
        }
    }

    // 统计未被照亮的格子数量
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            if (a[i][j] == 0)
                ans++;
        }
    }

    cout << ans;                // 输出未被照亮的格子总数

    return 0;                   // 程序正常结束
}

【运行结果】

5 1 0
3 3
12
posted @ 2026-08-30 09:18  团爸讲算法  阅读(10)  评论(0)    收藏  举报