Go Straight

Go Straight

1. 问题重述

给定一个 H×W 的网格,每个格子有类型:

  • #:不可进入
  • .、S、G:可自由进出
  • o:进入该格子后必须沿上一步方向继续前进(不能转向)
  • x:进入该格子后必须改变方向(不能沿原方向直走)

起点为 S,终点为 G。求一条从 S 到 G 的合法移动序列,移动次数 ≤ 5×10⁶。若不可能则输出 No。

2. 核心难点

普通 BFS 只记录位置,但本题的移动规则依赖于上一步的方向。因此,状态必须包含“当前位置”和“进入该格子时的方向”。

3. 状态定义

用三元组 (i, j, dir) 表示:

  • 当前位于格子 (i, j)(行、列)
  • 上一步移动的方向为 dir(0:上, 1:下, 2:左, 3:右)

方向 dir 是指“从上一个格子走到当前格子所采用的方向”,即进入当前格子时的方向。

共有 4×H×W 个状态,最多 4×10⁶,可以接受。

4. 状态转移

对于当前状态 (x, y, d),尝试向四个方向 nd ∈ {0,1,2,3} 走一步,得到 (nx, ny):

  • 若 (nx, ny) 出界或为 #,跳过
  • 若 (nx, ny) 已以方向 nd 访问过,跳过(vis[nx][ny][nd] == 1)

根据当前格子 (x,y) 的类型决定允许的 nd:

  • 若 g[x][y] == 'o':只允许 nd == d(直走)
  • 若 g[x][y] == 'x':只允许 nd != d(改变方向,包括掉头)
  • 若 g[x][y] 为 .、S、G:允许所有 nd

若允许,则标记 vis[nx][ny][nd] = 1,记录前驱 pre[nx][ny][nd] = d,并将 (nx, ny, nd) 入队。

注意:S 格子本身没有“上一步方向”,所以初始时我们从 S 向四个方向各走一步,得到四个初始状态 (nx, ny, d),其前驱方向设为 -1(表示起点)。

5. BFS 搜索

使用队列进行 BFS(因为边权为 1,可保证路径最短,但本题不要求最短,任意可达路径即可,但 BFS 简单且长度可控)。

在 BFS 过程中,当从队列中取出一个状态时,先检查该状态的位置是否就是 G,若是则说明已到达终点,记录当前方向 d 并结束搜索。否则继续扩展。

若队列为空仍未找到,则输出 No。

6. 路径重建

当找到终点状态 (Gx, Gy, goal_dir) 后,利用 pre 数组回溯:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int dx[4] = {-1, 1, 0, 0};
const int dy[4] = {0, 0, -1, 1};
char dirchar[4] = {'U','D','L','R'};
int vis[1000][1000][4];
int pre[1000][1000][4];

void solve() {
    int h, w; cin >> h >> w;
    vector<string> g(h);
    for(int i = 0; i < h; i ++) cin >> g[i];
    pair<int, int> S, G;
    for(int i = 0; i < h; i ++) for(int j = 0; j < w; j ++) {
        if(g[i][j] == 'S') S = {i, j};
        if(g[i][j] == 'G') G = {i, j};
    }

    memset(vis, 0, sizeof(vis));
    memset(pre, -1, sizeof(pre));
    queue<tuple<int, int, int>> q;
    for(int i = 0; i < 4; i ++) {
        int x = S.first + dx[i], y = S.second + dy[i];
        if(x < 0 || y < 0 || x >= h || y >= w || g[x][y] == '#') continue;
        q.push({x, y, i});
        vis[x][y][i] = 1;
        pre[x][y][i] = -1;
    }

    bool goat = false;
    int dir = -1;
    while(!q.empty()) {
        auto [x, y, d] = q.front(); q.pop();
        if(x == G.first && y == G.second) {goat = true; dir = d; break;}
        for(int i = 0; i < 4; i ++) {
            int tx = x + dx[i], ty = y + dy[i];
            if(tx < 0 || ty < 0 || tx >= h || ty >= w) continue;
            if(g[tx][ty] == '#' || vis[tx][ty][i]) continue;
            if(g[x][y] == 'x' && i == d) continue;
            if(g[x][y] == 'o' && i != d) continue; 

            vis[tx][ty][i] = 1;
            q.push({tx, ty, i});
            pre[tx][ty][i] = d;
            if(tx == G.first && ty == G.second) {goat = true; dir = i; break;}
        }
        if(goat) break;
    }

    if(!goat) {cout << "No\n";return;}

    vector<int> path;
    int cur = dir;
    int wx = G.first, wy = G.second;
    while(cur != -2) {
        path.push_back(cur);
        int pd = pre[wx][wy][cur];
        if(pd == -1) break;
        wx -= dx[cur], wy -= dy[cur];
        cur = pd;
    }
    reverse(path.begin(), path.end());
    cout << "Yes\n";
    for(auto & i : path) cout << dirchar[i];
    cout << "\n";

}

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int _ = 1; //cin >> _;
    while(_--) solve();

    return 0;
}
posted @ 2026-11-03 22:12  thenrify  阅读(3)  评论(0)    收藏  举报