F. Turtle Mission: Robot and the Earthquake (codeforces 923 div.3)(bfs)

题意

.给定一个二维矩阵,每个格子为 0 0 0 1 1 1 表示该位置是空地还是石头,一个机器人从左上角( 0 0 0 , 0 0 0) 出发,每秒可以选择向上下右三个方向移动,并且地图的上下边界是循环的(即可以从地图的最下面一行向下走一步到达地图的上边界)。
在机器人移动的同时,地图上的石头每秒也往上走一格,并且也可以穿过边界循环到达另一边。在这样的前提下,求机器人能否到达地图的右下角( n − 1 n - 1 n1, m − 1 m - 1 m1)处,能的话输出到达的最小时间。

分析

看到题目,首先很容易想到这是一个BFS广搜的题,假如去掉石头会移动这个条件,那么这就是一个BFS裸题,直接把起点加入队列,每次往三个方向拓展,判断是否有石头再加入队列,队列里第一个到达终点的结点就是最短距离。但这题就是难在了石头会移动,直接BFS时不容易判断这一步拓展是否会撞到石头。
解决办法也很简单,既然所有的石头都向上循环移动一格,那么我们可以直接视作整个地图向上循环移动了一格。那么等效的,我们令机器人每秒固定向下走一格,就抵消了这个影响,就可以认为地图上的石头不动。这也是这题的核心。
于是题目相当于转化为,在一个上下循环的地图上,一个机器人从左上角出发,每次可以选择向下走两格,或者向右下斜着走一格,或者原地不动,问走到初始时地图的右下角的最短时间。
这里还要注意一个地方就是,由于我们把运动进行相对转化了,所以初始时的终点 ( n − 1 n - 1 n1, m − 1 m - 1 m1) 也在每秒移动,又由于地图的左右是不循环的,所以如果BFS过程中,机器人到达了最右边一列,我们再考虑回去原本的石头情况,那么接下来机器人只需要随着石头向上走,过一段时间总能够到达右下角点。
于是我们在BFS的时候,要对所有能够到达最右边一列的结点进行讨论,从该节点抵达最右边一列的时刻开始,再经过多长时间能够运动到原本的终点。其实这也不难,因为我们在BFS过程中已经知道了该结点BFS到这个时候经过的时间 d i s [ x ] [ y ] dis[x][y] dis[x][y] ,就可以算出原本的终点偏移后的位置,再做差取绝对值,就可以求出该节点距离偏移后的终点的距离,也就是该节点对应的答案值需要额外增加的时间。

代码

具体代码如下所示,由于我习惯数组下标从1开始不是从0开始,导致判断越界循环移动时会稍微麻烦一点。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std;
const int maxn = 1e3 + 10;
int arr[maxn][maxn], dis[maxn][maxn];
void solve() {
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++) {
            cin >> arr[i][j];
            dis[i][j] = -1; // 初始化距离矩阵
        }
    dis[1][1] = 0; // 起点的距离设为0
    queue<pii> q;
    q.push(pii(1, 1));
    int ans = inf; //初始化答案为最大值
    while (!q.empty()) {
        auto [x, y] = q.front();
        q.pop();
        if (y == m) { // 结点到达最右边一列时更新答案
            ans = min(ans, dis[x][y] + ((x - (n + dis[x][y])) % n + n) % n);
        }
        int nx = x % n + 1, ny = y + 1; // nx, ny表示移动后的坐标
        if (ny <= m && arr[nx][ny] == 0 && dis[nx][ny] == -1) { // 移动不越界,并且没有石头的情况下移动
            dis[nx][ny] = dis[x][y] + 1;
            q.push(pii(nx, ny));
        }
        nx = (x + 2) % n, ny = y;
        if (nx == 0) // 判断循环移动坐标
            nx = n;
        if (arr[x % n + 1][y] == 0 && arr[nx][ny] == 0 && dis[nx][ny] == -1) {
            dis[nx][ny] = dis[x][y] + 1;
            q.push(pii(nx, ny));
        }
    }
    if (ans == inf) // 答案未被更新过表示无法到达
        ans = -1;
    cout << ans << endl;
}
signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t;
    cin >> t;
    while (t--)
        solve();
    return 0;
}
  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值