题解:洛谷 P1789 插火把
【题目来源】
【题目描述】
话说有一天 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
【核心思想】
-
问题分析:在一个 \(n \times n\) 的网格中,放置了若干火把和萤石。火把照亮一个十字形加四个对角格子的区域(共 13 格,但需考虑边界),萤石照亮一个 \(5 \times 5\) 的正方形区域(共 25 格,需考虑边界)。需要统计网格中没有被任何光源照亮的格子数量(这些格子会生成怪物)。本质是一个二维网格覆盖标记问题,可以使用布尔数组标记所有被照亮的格子,最后统计未标记的格子数。
-
算法选择:
- 标记数组模拟:使用一个二维布尔数组
light[1..n][1..n]初始为false,表示未被照亮。对于每个光源,根据其类型和位置,将对应照亮范围内的格子标记为true。最后遍历整个网格,统计false的数量。 - 边界处理:在标记时需检查坐标是否在 \([1, n]\) 范围内,避免数组越界。
- 标记数组模拟:使用一个二维布尔数组
-
关键步骤:
- 读入与初始化:读取 \(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的数量并输出。
- 读入与初始化:读取 \(n, m, k\),将
-
时间/空间复杂度:
- 时间复杂度:\(O(n^2 + (m+k) \cdot R)\),其中 \(R\) 是每个光源的照亮面积(常数),\(n \le 100\),完全可行。
- 空间复杂度:\(O(n^2)\)。
-
模拟与网格覆盖:
- 标记法的通用性:用数组模拟网格状态是处理此类“覆盖/照亮”问题的标准方法。
- 边界检查:在标记过程中必须检查坐标是否在 \([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
浙公网安备 33010602011771号