0%

2258. 逃离火灾

2258.逃离火灾

题目描述:

给你一个下标从 0 开始大小为 m x n 的二维整数数组 grid ,它表示一个网格图。每个格子为下面 3 个值之一:

  • 0 表示草地。
  • 1 表示着火的格子。
  • 2 表示一座墙,你跟火都不能通过这个格子。

一开始你在最左上角的格子 (0, 0) ,你想要到达最右下角的安全屋格子 (m - 1, n - 1) 。每一分钟,你可以移动到 相邻 的草地格子。每次你移动 之后 ,着火的格子会扩散到所有不是墙的 相邻 格子。

请你返回你在初始位置可以停留的 最多 分钟数,且停留完这段时间后你还能安全到达安全屋。如果无法实现,请你返回 -1 。如果不管你在初始位置停留多久,你 总是 能到达安全屋,请你返回 109

注意,如果你到达安全屋后,火马上到了安全屋,这视为你能够安全到达安全屋。

如果两个格子有共同边,那么它们为 相邻 格子。

数据范围:

$2\le m, n \le 300, 4\le m \times n \le 2 \times 10^4$ .

题解:

复杂度只支持 $n\log n$ 的做法。

可以先试用多源bfs得到每个点被火覆盖的时间。

多源bfs和单源类似,直接把所有的起点入队,每次第一次遍历到一个未访问过的节点时就是最短的。也可以加上判断是不是最短路,判断有几条最短路经过了该点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
queue<int> q;
int bfs()
{
while(!q.empty())
{
int out = q.front(); q.pop();
if(times[out] == m) return out; // 这个点更新了m次,找到一个最先更新m次的点
// 由于每个点只能更新一次,所以更新m次时一定是找到了
for(int i = head[out]; i; i = edge[i].next)
{
int v = edge[i].v;
// 注意这里我写的是0,因为初始化的时候填的是1,找没有走过的,或者下一步的
if(dis[v] == 0 || dis[v] == dis[out] + 1) // 保证是最短路更新的。
{
dis[v] = dis[out] + 1;
times[v] += times[out]; // 注意是加等于,因为可能有几个点更新之后到达了同一个位置,继续更新时只能加。

if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
return -1;
}

多源bfs得到每个点被火覆盖的时间之后,需要从起点走到终点。发现具有二分单调性,停留的时间越短,能够通过,越多,越不能通过。

因此可以二分停留的时间,然后使用bfs check能否从起点走到终点。假设停留了 $mid$ 分钟,那么花费了 $t$ 到达 $v$ ,则如果 $grid[v] <= t + 1$ 则说明火已经覆盖到 $v$ , $v$ 不能扩展。但是终点是例外的,终点如果恰好被火覆盖是能逃跑的。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

auto optimize_cpp_stdio = []()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
return 0;
}();
class Solution
{
public:
const static int maxn = 1e5 + 10;
const static int maxm = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> dx = {-1, 0, 1, 0}, dy = {0, 1, 0, -1};
bool check(int mid, vector<vector<int>> &grid)
{
if (grid[0][0] <= mid)
return false;
int n = grid.size(), m = grid[0].size();
queue<pair<int, int>> q;
vector<vector<bool>> vis(n, vector<bool>(m, false));
q.push({0, mid});
vis[0][0] = true;
while (q.size())
{
auto out = q.front();
q.pop();
int x = out.first / m;
int y = out.first % m;
int dis = out.second;
if (x == n - 1 && y == m - 1)
return true;
for (int i = 0; i < 4; ++i)
{
int newx = x + dx[i];
int newy = y + dy[i];
if (newx == n - 1 && newy == m - 1 && grid[newx][newy] >= dis + 1)
return true;
if (newx < 0 || newx >= n || newy < 0 || newy >= m || grid[newx][newy] <= 0 ||
grid[newx][newy] <= dis + 1 || vis[newx][newy])
continue;
q.push({newx * m + newy, dis + 1});
vis[newx][newy] = true;
}
}
return false;
}
int maximumMinutes(vector<vector<int>> &grid)
{
int n = grid.size(), m = grid[0].size();
queue<pair<int, int>> q;

for (int i = 0; i < n; ++i)
{
for (int j = 0; j < m; ++j)
{
if (grid[i][j] == 2)
grid[i][j] = -2;
if (grid[i][j] == 0)
grid[i][j] = INF;
if (grid[i][j] == 1)
{
grid[i][j] = 0;
q.push({i, j});
}
}
}
while (q.size())
{
auto out = q.front();
q.pop();
for (int i = 0; i < 4; ++i)
{
int newx = out.first + dx[i];
int newy = out.second + dy[i];
if (newx < 0 || newx >= n || newy < 0 || newy >= m || grid[newx][newy] <= 0 || grid[newx][newy] != INF)
continue;
grid[newx][newy] = min(grid[newx][newy], grid[out.first][out.second] + 1);
q.push({newx, newy});
}
}
// if (!check(0, grid))
// return -1;
int l = 0, r = m * n, ans = -1;
while (l <= r)
{
int mid = (l + r) >> 1;
if (check(mid, grid))
{
ans = mid;
l = mid + 1;
}
else
r = mid - 1;
}
return ans == m * n ? 1e9 : ans;
}
};