給定 l × l 棋盤 (4 ≤ l ≤ 300) 及起點、終點座標,求國際象棋「騎士」從起點移動到終點的最少步數。騎士移動方式為 8 向「日」字走法。
O(b^d) 降為 O(b^(d/2)),b=8、d 為答案步數,極大加速。const int MAXL = 330;
int vis1[MAXL][MAXL]; // 起點端距離,-1 表未訪問
int vis2[MAXL][MAXL]; // 終點端距離
int dx[8] = {2,2,-2,-2,1,1,-1,-1};
int dy[8] = {1,-1,1,-1,2,-2,2,-2};
struct Node { int x, y; };
void biBFS(int sx, int sy, int ex, int ey, int l) {
memset(vis1, -1, sizeof(vis1));
memset(vis2, -1, sizeof(vis2));
queue<Node> q, w;
q.push({sx, sy}); vis1[sx][sy] = 0;
w.push({ex, ey}); vis2[ex][ey] = 0;
while (!q.empty() && !w.empty()) {
// 擴展起點端一層
int qs = q.size();
while (qs--) {
Node cur = q.front(); q.pop();
if (vis2[cur.x][cur.y] != -1) { // 相遇
cout << vis1[cur.x][cur.y] + vis2[cur.x][cur.y] << '\n';
return;
}
for (int i=0;i<8;i++) {
int nx = cur.x + dx[i], ny = cur.y + dy[i];
if (nx<0||nx>=l||ny<0||ny>=l) continue;
if (vis2[nx][ny] != -1) { // 下一層碰到終點端
cout << vis1[cur.x][cur.y] + 1 + vis2[nx][ny] << '\n';
return;
}
if (vis1[nx][ny] == -1) {
vis1[nx][ny] = vis1[cur.x][cur.y] + 1;
q.push({nx, ny});
}
}
}
// 擴展終點端一層 (對稱邏輯)
int ws = w.size();
while (ws--) {
Node cur = w.front(); w.pop();
if (vis1[cur.x][cur.y] != -1) {
cout << vis1[cur.x][cur.y] + vis2[cur.x][cur.y] << '\n';
return;
}
for (int i=0;i<8;i++) {
int nx = cur.x + dx[i], ny = cur.y + dy[i];
if (nx<0||nx>=l||ny<0||ny>=l) continue;
if (vis1[nx][ny] != -1) {
cout << vis2[cur.x][cur.y] + 1 + vis1[nx][ny] << '\n';
return;
}
if (vis2[nx][ny] == -1) {
vis2[nx][ny] = vis2[cur.x][cur.y] + 1;
w.push({nx, ny});
}
}
}
}
}
O(l^2) 最壞全盤遍歷,但雙向 BFS 實際訪問節點數約為單向 BFS 平方根。l×l 整數陣列 + 兩佇列,l≤300 時約 2×300×300×4B ≈ 720KB,極小。Input:
3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1
Output:
5
28
0
vis 陣列也可 AC,但 l=300 時最壞訪問 90,000 節點,仍在時限內;雙向 BFS 更穩健。size() 控制),否則無法保證最短路徑層數正確。