[2020CCPC威海B] Labyrinth - 结论,BFS

Description

给定一张 \(n \times m\) 的棋盘,上面有 \(k\) 个障碍物,有 \(q\) 次询问,每次给定两个点 \((x_1,y_1),\ (x_2,y_2)\),问这两点之间的最短路径长度。\(n\cdot m \le 2 \times 10^5, k \le 42, q \le 10^5\)

Solution

对于 \((x_1,y_1),\ (x_2,y_2)\) 两点构成的最短路径,如果大于忽略障碍物时的曼哈顿距离,则一定有一条贴着障碍物的路径与它等长。

所谓贴着障碍物,就是路径上至少存在一个点,是障碍物的邻居。

因此,如果矩形 \((x_1,y_1) - (x_2,y_2)\) 中没有障碍物,我们就直接用曼哈顿距离;否则,找到一个障碍物的邻居点 \((x_t,y_t)\),使得路径 \((x_1,y_1) \to (x_t,y_t) \to (x_2,y_2)\) 最短。

判断矩形中是否有障碍物显然可以暴力实现。

求最短距离,只需要预处理每一个障碍物邻居点到棋盘上所有点的最短距离即可。

#include <bits/stdc++.h>
using namespace std;

const int N = 400005;
const int inf = 0x3f3f3f3f;
const int dx[4] = {1, -1, 0, 0};
const int dy[4] = {0, 0, -1, 1};

set<pair<int, int>> s;
int *dis[50][4][N];

vector<pair<int, int>> holes;

int n, m, k, q;

struct Item
{
    int x, y, d;
};

int check(int x1, int y1, int x2, int y2)
{
    if (x1 > x2)
        swap(x1, x2);
    if (y1 > y2)
        swap(y1, y2);
    for (auto hole : holes)
    {
        int nx = hole.first, ny = hole.second;
        if (x1 <= nx && nx <= x2 && y1 <= ny && ny <= y2)
            return true;
    }
    return false;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin >> n >> m >> k >> q;
    for (int i = 1; i <= k; i++)
    {
        int t1, t2;
        cin >> t1 >> t2;
        holes.push_back({t1, t2});
        s.insert({t1, t2});
    }
    for (int holeId = 0; holeId < holes.size(); holeId++)
    {
        auto hole = holes[holeId];
        int x = hole.first, y = hole.second;
        for (int pos = 0; pos < 4; pos++)
        {
            int nx = x + dx[pos], ny = y + dy[pos];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m)
            {
                for (int i = 0; i <= n + 1; i++)
                {
                    dis[holeId][pos][i] = new int[m + 5];
                    for (int j = 0; j <= m + 1; j++)
                        dis[holeId][pos][i][j] = inf;
                }
                if (s.find({nx, ny}) == s.end())
                {
                    queue<Item> que;
                    que.push({nx, ny, 0});
                    dis[holeId][pos][nx][ny] = 0;
                    while (que.size())
                    {
                        int nx = que.front().x, ny = que.front().y, nd = que.front().d;
                        que.pop();
                        for (int p = 0; p < 4; p++)
                        {
                            int tx = nx + dx[p], ty = ny + dy[p], td = nd + 1;
                            if (tx >= 1 && tx <= n && ty >= 1 && ty <= m && s.find({tx, ty}) == s.end()) if (dis[holeId][pos][tx][ty] == inf)
                            {
                                dis[holeId][pos][tx][ty] = td;
                                que.push({tx, ty, td});
                            }
                        }
                    }
                }
            }
        }
    }
    for (int t = 1; t <= q; t++)
    {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        if (check(x1, y1, x2, y2))
        {
            int ans = inf;
            for (int i = 0; i < k; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    int nx = holes[i].first + dx[j], ny = holes[i].second + dy[j];
                    if (1 <= nx && nx <= n && 1 <= ny && ny <= m)
                    {
                        ans = min(ans, dis[i][j][x1][y1] + dis[i][j][x2][y2]);
                    }
                }
            }
            if (ans < inf)
                cout << ans << endl;
            else
                cout << -1 << endl;
        }
        else
            cout << abs(x1 - x2) + abs(y1 - y2) << endl;
    }
    return 0;
}
上一篇:scanf_s获取屏幕不同类型数据的正确方法演示


下一篇:对于数独问题的专题探究