方格探索(双端队列)

题意

给定一个\(n\)\(m\)列的方格矩阵。行坐标从上到下为\(1 \sim n\),列坐标从左到右为\(1 \sim m\)。其中的每个方格,要么是空格(用.表示),要么包含障碍物(用*表示)。

初始时,一个人位于第\(r\)行第\(c\)列的空格之中。他可以沿上下左右四个方向进行移动,每次移动一格距离。

对于他的移动,有如下限制:

  • 他不能进入到包含障碍物的方格中,也不能走出矩阵的边界。
  • 在整个移动过程中,他向左移动的总次数不能超过\(x\)次。
  • 在整个移动过程中,他向右移动的总次数不能超过\(y\)次。

请问,一共有多少个空格是此人可以抵达的?注意,初始空格视为此人可达。

题目链接:https://www.acwing.com/problem/content/4484/

数据范围

\(1 \leq n, m \leq 2000\)
\(0 \leq x, y \leq 10^9\)

思路

首先想到的是利用分层图求解,但是由于\(x, y\)太大,因此时空复杂度都不满足条件,因此考虑如何进行优化。

我们可以先考虑一个简单的问题,只对向左移动的总次数有限制,对向右移动次数没有限制。

这个问题是一个最短路问题,向左走时边权为\(1\),其他方向边权为\(0\)。注意:在BFS时要使用双端队列,维护一个st数组,同时将向左走的总次数传入队列中。

现在回到原来的问题。如何才能同时处理左右都有限制的情况呢?

我们发现一个性质,从\((r, c)\)走到任意一个点\((i, j)\),有着\(right - left = j - c\)恒成立,其中\(right\)为向右走的步数,\(left\)为向左走的步数。因此,向左走的总次数最短,意味着向右走的总次数也是最短的。因此,比起简化版的问题,只需要在队列中同时传入向左走的总次数和向右走的总次数。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>

#define x first
#define y second

using namespace std;

typedef pair<int, int> pii;
typedef pair<pii, pii> ppp;

const int N = 2010;

int n, m, r, c, L, R;
char g[N][N];
bool st[N][N];

int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};

void bfs()
{
    deque<ppp> que;
    que.push_back({{r, c}, {0, 0}});
    st[r][c] = true;
    while(que.size()) {
        auto t = que.front();
        que.pop_front();
        int tx = t.x.x, ty = t.x.y;
        for(int i = 0; i < 4; i ++) {
            int x = tx + dx[i], y = ty + dy[i];
            if(x < 1 || x > n || y < 1 || y > m) continue;
            if(g[x][y] == '*') continue;
            if(st[x][y]) continue;
            if(i == 0 || i == 1) {
                que.push_front({{x, y}, {t.y.x, t.y.y}});
                st[x][y] = true;
            }
            else if(i == 2) {
                if(t.y.x + 1 > L) continue;
                que.push_back({{x, y}, {t.y.x + 1, t.y.y}});
                st[x][y] = true;
            }
            else {
                if(t.y.y + 1 > R) continue;
                que.push_back({{x, y}, {t.y.x, t.y.y + 1}});
                st[x][y] = true;
            }
        }
    }
}

int main()
{
    scanf("%d%d%d%d%d%d", &n, &m, &r, &c, &L, &R);
    for(int i = 1; i <= n; i ++) scanf("%s", g[i] + 1);
    bfs();
    int ans = 0;
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= m; j ++) {
            if(st[i][j]) {
                ans ++;
            }
        }
    }
    printf("%d\n", ans);
    return 0;
}
posted @ 2022-06-12 15:30  pbc的成长之路  阅读(32)  评论(0编辑  收藏  举报