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;
}

浙公网安备 33010602011771号