魔法之森的蘑菇 bfs

文章讲述了在牛客周赛中如何运用三维数组和BFS算法处理有障碍物和转向规则的路径问题。
摘要由CSDN通过智能技术生成

E-魔法之森的蘑菇_牛客周赛 Round 37 (nowcoder.com)

问题描述:经典bfs(有障碍物的权值相同最短路)改,额外加了一个条件,遇到.不能转向,只能向同一方向直走,遇到*可以转向,但是不能调头(往回走)。

我刚开始做的时候,直接设置二维数组表示 ( x , y ) (x, y) (x,y),加上限制条件走bfs。对了百分之二三十,以为bfs写错了,一直调也没有发现错误。无奈看题解。

看了题解才发现我太惯性思维了,点 ( x , y ) (x, y) (x,y)上不止一种状态。点 ( x , y ) (x, y) (x,y)上会有 ( x , y , l e f t ) (x, y, left) (x,y,left) ( x , y , r i g h t ) (x, y, right) (x,y,right) ( x , y , u p ) (x, y, up) (x,y,up) ( x , y , d o w n ) (x, y, down) (x,y,down),即由 ( x i , y i , N U L L ) (x_i, y_i, NULL) (xi,yi,NULL) ( x , y ) (x, y) (x,y)的方向是不一样的,根据限制条件, ( x , y ) (x, y) (x,y)的行动也是不一样的。

因此在前置基础上加上方向,设为三维数组进行bfs即可。

代码如下:(指向的处理大部分是原来二维时写的,可以用dir找前驱到该点的编号进行优化以此简化代码)

#include <bits/stdc++.h>
using namespace std;
int fx[] = {0, 0, 1, -1};
int fy[] = {1, -1, 0, 0};
struct no {
    int x,y,ox,oy,cnt;
};
void solve() {
    int n,m; cin>>n>>m;
    vector<string> ph(n);
    for(auto &t: ph) cin>>t;
    // 三维数组
    vector<vector<vector<int>>> vis(n, vector<vector<int>>(m, vector<int>(5,0)));
    queue<no> q;
    int stx, sty;
    for(int i = 0; i < n; ++i) {
        for(int j = 0; j < m; ++j) {
            if(ph[i][j] == 'S') {
                stx = i, sty = j;
                break;
            }
        }
    }
    // 计算 (nowx, nowy) <--> (ox, oy)
    // 指向无所谓,只要指向都用一种都可以(严格上用 (ox, oy) --> (nowx, nowy) ,因为有具体的含义
    auto dir = [&](int nowx, int nowy, int ox, int oy) ->int {
        for(int i = 0; i < 4; ++i) {
            if(nowx + fx[i] == ox && nowy + fy[i] == oy) return i;
        }
        // 无效的返回值 (牛客编辑器会提醒,太良心了
        return -1;
    };
    // 先搜起点,起点特殊,没有前驱
    for(int i = 0; i < 4; ++i) {
        int xx = stx + fx[i], yy = sty + fy[i];
        if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
        if(ph[xx][yy] == '#') continue;
        if(ph[xx][yy] == 'T') {
            cout<<1<<'\n';
            return ;
        }
        q.push({xx, yy, stx, sty, 1});
        vis[xx][yy][dir(xx,yy,stx,sty)] = true;
    }
    // 开始bfs
    while(q.size()) {
        auto tmp = q.front(); q.pop();
        int x = tmp.x, y = tmp.y, ox = tmp.ox, oy = tmp.oy, lastcnt = tmp.cnt;
        for(int i = 0; i < 4; ++i) {
            int xx = x + fx[i], yy = y + fy[i];
            if(xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
            if(ph[xx][yy] == '#') continue;

            // "." 和 "*" 分开处理
            if(ph[x][y] == '.') {
                if(xx - x == x - ox && yy - y == y - oy) {
                    // 如果是终点,直接输出
                    if(ph[xx][yy] == 'T') {
                        cout<<lastcnt + 1<<'\n';
                        return ;
                    } else {
                        if(vis[xx][yy][dir(xx, yy, x, y)]) continue;
                        q.push({xx, yy, x, y, lastcnt + 1});
                        // 如果满足 "."的限制条件可以走(xx, yy) 才可以设置为true
                        vis[xx][yy][dir(xx, yy, x, y)] = true;
                    }
                }
                // 同 "."的处理思路
            } else if(ph[x][y] == '*') {
                if(xx != ox || yy != oy) {
                    if(ph[xx][yy] == 'T') {
                        cout<<lastcnt + 1<<'\n';
                        return ;
                    } else {
                        if(vis[xx][yy][dir(xx, yy, x, y)]) continue;
                        q.push({xx, yy, x, y, lastcnt + 1});
                        vis[xx][yy][dir(xx, yy, x, y)] = 1;
                    }
                }
            }
        }
    }
    cout<<-1<<'\n';
    
}
int main()
{
    int t; cin>>t;
    while(t--) solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

golemon.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值