MENU

UVA11624 Fire! (双重BFS)

2018 年 07 月 24 日 • 阅读: 1025 • 图论阅读设置

Fire!

Joe works in a maze. Unfortunately, portions of the maze have caught on fire, and the owner of the maze neglected to create a fire escape plan. Help Joe escape the maze.

Given Joe’s location in the maze and which squares of the maze are on fire, you must determine whether Joe can exit the maze before the fire reaches him, and how fast he can do it.

Joe and the fire each move one square per minute, vertically or horizontally (not diagonally). The fire spreads all four directions from each square that is on fire. Joe may exit the maze from any square that borders the edge of the maze. Neither Joe nor the fire may enter a square that is occupied by a wall.

Input

The first line of input contains a single integer, the number of test cases to follow. The first line of each test case contains the two integers R and C, separated by spaces, with 1 ≤ R, C ≤ 1000. The following R lines of the test case each contain one row of the maze. Each of these lines contains exactly C characters, and each of these characters is one of:

• #, a wall

• ., a passable square

• J, Joe’s initial position in the maze, which is a passable square

• F, a square that is on fire

There will be exactly one J in each test case.

Output

For each test case, output a single line containing ‘IMPOSSIBLE’ if Joe cannot exit the maze before the fire reaches him, or an integer giving the earliest time Joe can safely exit the maze, in minutes.

Sample Input

2
4 4
####
#JF#
#..#
#..#
3 3
###
#J.
#.F

Sample Output

3
IMPOSSIBLE

链接

https://vjudge.net/problem/UVA-11624

题意

现有一个迷宫,其中有多个格子着火。Joe需要走出这个迷宫(到达边界格子),Joe可以走上下左右四个方向,但是目前已经着火的格子也会向上下左右四个方向蔓延,此外,迷宫中有一些障碍,Joe和火都无法进入。求Joe走出迷宫的最短时间,不能走出的话输出“IMPOSSIBLE”。

题解

如果没有火,那么本题是一个标准的迷宫问题,可以使用BFS解决。加上了火,难度其实没有增加多少,因为火是不会自动熄灭的,所以只需要预处理每个格子起火的时间在BFS扩展结点的时候加一个判断,当到达新结点时该格子没着火才把这个新结点加入队列中

最后需要考虑一下如何求出每个格子起火的时间,其实这也是一个最短路问题,只不过起点不是一个,而是多个(所有的初始着火点)。只需要在队列初始化时把所有着火点都放进去即可。

两个步骤的时间复杂度均为O(RC)。——小白书P307

这位博主写的挺好的,传送门

代码

StatusAccepted
Time130ms
Length2464
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 1010;
const int inf = 0x3f3f3f3f;
char G[maxn][maxn];
bool vis[maxn][maxn];
int dir[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
int Time[maxn][maxn];
int n, m;

struct Node
{
    int x, y, t;
    Node(){}
    Node(int _x, int _y, int _t) {x = _x, y = _y, t = _t;}
};

queue <Node> Fire;
queue <Node> Joe;

void init() // 初始化
{
    while (!Fire.empty())
        Fire.pop();
    while (!Joe.empty())
        Joe.pop();
    memset(vis, 0, sizeof(vis));
    memset(Time, 0x3f, sizeof(Time));
}

void bfs_fire() // bfs得到每个点起火的最短时间
{
    while (!Fire.empty())
    {
        Node u = Fire.front();
        Fire.pop();
        for (int i = 0; i < 4; ++i)
        {
            int xx = u.x + dir[i][0], yy = u.y + dir[i][1], tt = u.t + 1;
            if (xx >= 1 && xx <= n && yy >= 1 && yy <= m && (G[xx][yy] == '.' || G[xx][yy] == 'J') && tt < Time[xx][yy])
            {
                Time[xx][yy] = tt;
                Fire.push(Node(xx, yy, tt));
            }
        }
    }
}

int escape() // bfs寻找出口,但是需要判断该点是否已经起火
{
    while (!Joe.empty())
    {
        Node u = Joe.front();
        Joe.pop();
        if (u.x == 1 || u.x == n || u.y == 1 || u.y == m)
            return u.t + 1;
        for (int i = 0; i < 4; ++i)
        {
            Node v(u.x + dir[i][0], u.y + dir[i][1], u.t + 1);
            int xx = v.x, yy = v.y, tt = v.t;
            if (xx >= 1 && xx <= n && yy >= 1 && yy <= m && !vis[xx][yy] && G[xx][yy] == '.' && tt < Time[xx][yy])
            {
                vis[xx][yy] = true;
                Joe.push(v);
            }
        }
    }
    return 0;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d%d", &n, &m);
        init();
        for (int i = 1; i <= n; ++i)
        {
            scanf("%s", G[i]+1); // 注意,从1开始的话是G[i]+1
            for (int j = 1; j <= m; ++j)
            {
                if (G[i][j] == 'F') // 多个着火点入队,不需要vis标记,因为我们是用着火的最短时间来判断
                    Fire.push(Node(i, j, 0));
                else if (G[i][j] == 'J')
                {
                    Joe.push(Node(i, j, 0)); // 初始位置
                    vis[i][j] = true;
                }
            }
        }
        bfs_fire(); // 预处理着火时间
        int ans = escape();
        if (ans > 0) // 得到走出迷宫最短时间
            printf("%d\n", ans);
        else
            printf("IMPOSSIBLE\n");
    }
    return 0;
}

和中南多校的那道题Barareh on Fire是差不多,但是火势是向八个方向在k秒内蔓延,也贴一下代码。

#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 110;
const int inf = 0x3f3f3f3f;
int n, m, k;
int map[maxn][maxn];
int fire[maxn][maxn];
int dirf[8][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, -1}, {1, 1}};
int dirm[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

void init()
{
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j)
            fire[i][j] = map[i][j] = inf;
}

struct node
{
    int x, y;
};
queue <node> q;

int main()
{
    while(cin >> n >> m >> k)
    {
        if (n == 0 && m == 0 && k == 0)
            break;
        init();
        char ch;
        node s, t, u, v;
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= m; ++j)
            {
                cin >> ch;
                if (ch == 'f')
                {
                    fire[i][j] = 0;
                    u.x = i, u.y = j;
                    q.push(u);
                }
                else if (ch == 's')
                {
                    map[i][j] = 0;
                    s.x = i, s.y = j;
                }
                else if (ch == 't')
                    t.x = i, t.y = j;
            }
        }
        while (!q.empty()) // 预处理
        {
            u = q.front();
            q.pop();
            for (int i = 0; i < 8; ++i)
            {
                v.x = u.x + dirf[i][0];
                v.y = u.y + dirf[i][1];
                if (fire[v.x][v.y] > fire[u.x][u.y] + k)
                {
                    fire[v.x][v.y] = fire[u.x][u.y] + k;
                    q.push(v);
                }
            }
        }
        q.push(s);
        while (!q.empty()) // 迷宫问题
        {
            u = q.front();
            q.pop();
            for (int i = 0; i < 4; ++i)
            {
                v.x = u.x + dirf[i][0];
                v.y = u.y + dirf[i][1];
                if (v.x <= n && v.x >= 1 && v.y >= 1 && v.y <= m)
                {
                    if (map[u.x][u.y] + 1 >= fire[v.x][v.y]) // 着火区域
                        continue;
                    if (map[v.x][v.y] > map[u.x][u.y] + 1)
                    {
                        map[v.x][v.y] = map[u.x][u.y] + 1;
                        q.push(v);
                    }
                }
            }
        }
        if (map[t.x][t.y] == inf)
            cout << "Impossible" << endl;
        else
            cout << map[t.x][t.y] << endl;
    }
    return 0;
}

The end.
2018-07-24 星期二
最后编辑于: 2018 年 11 月 02 日