N×M 網格,`#` 不可通行,`.` 可通行,`@` 起點。K≤4 個寶物位置給定。從起點出發收集所有寶物,不需返回,求最少步數。不可重複訪問格子?題目未明確,但 BFS 自然不重複訪問。
K ≤ 4,全排列數 ≤ 24。對每種順序,依次 BFS 從當前位置到下一寶物,累加距離。取最小總距離。
int bfs(int sx, int sy, int tx, int ty) {
queue<Node> q;
q.push({sx, sy, 0});
vis[sx][sy] = true;
while (!q.empty()) {
auto [x, y, d] = q.front(); q.pop();
if (x == tx && y == ty) return d;
for 4方向:
if 合法且未訪問且非'#':
vis[nx][ny] = true;
q.push({nx, ny, d+1});
}
return -1; // 不可達
}
原代碼在單次 BFS 中按順序尋找下一目標,到達後清空隊列重置 visited 繼續找下一個。這比每段單獨 BFS 更快(避免重複掃描公共前綴路徑)。
int bfs_sequence(const vector<Point> &targets) {
queue<Node> q;
q.push({start_x, start_y, 0});
memset(vis, 0, sizeof(vis));
vis[start_x][start_y] = 1;
int target_idx = 0;
while (!q.empty() && target_idx < targets.size()) {
auto [x, y, d] = q.front(); q.pop();
for 4方向:
nx, ny, nd = d+1
if 合法:
if (nx == targets[target_idx].x && ny == targets[target_idx].y) {
// 到達當前目標
target_idx++;
if (target_idx == targets.size()) return nd;
// 重置 BFS 狀態從當前位置繼續找下一目標
while (!q.empty()) q.pop();
memset(vis, 0, sizeof(vis));
q.push({nx, ny, nd});
vis[nx][ny] = 1;
break; // 跳出方向循環
}
vis[nx][ny] = 1;
q.push({nx, ny, nd});
}
return -1;
}
int main() {
while (cin >> n >> m && n) {
// 讀網格,找起點
cin >> K;
vector<Point> gems(K);
for (i=0..K-1) cin >> gems[i].x >> gems[i].y, --x, --y;
vector<int> perm(K);
iota(perm.begin(), perm.end(), 0);
int ans = -1;
do {
vector<Point> seq;
for (idx : perm) seq.push_back(gems[idx]);
int dist = bfs_sequence(seq);
if (dist != -1 && (ans == -1 || dist < ans)) ans = dist;
} while (next_permutation(perm.begin(), perm.end()));
cout << ans << "\n";
}
}
| 細節 | 處理 |
|---|---|
| 全排列生成 | `next_permutation` 需初始有序數組 |
| 下標轉換 | 輸入 1-indexed → 內部 0-indexed |
| BFS 重置 | 到達目標後清空隊列、重置 visited、從目標點繼續 |
| 不可達處理 | 該排列跳過 |
| K=0 | 題目保證 K>0 |
此題展示了「少量關鍵點 + 全排列枚舉順序 + 多目標 BFS」的經典解題模式,將 TSP 類問題在小 K 時完美解決。