3×3 八數碼問題:初始狀態到目標狀態 123456780(0 代表空格)。每步移動空格與相鄰數字交換。求最短移動序列(輸出操作字符 u/d/l/r),無解輸出 unsolvable。單測試案例(POJ)或多案例(HDU)。
將 9 位排列映射為 [0, 9!-1] 的整數:
rank = Σ_{i=0}^{8} (小於 a[i] 且在 i 後的數字個數) * (8-i)!
用於 visited 數組索引、去重、路徑回溯。
visited[rank]=true,dist[rank]=0,pre[rank] 存儲前驅狀態(或存儲完整棋盤用於回溯)。newRank,若未訪問則入隊,記錄前驅與操作。存儲每個狀態的完整棋盤 state[rank][9](空間 9! * 9 ≈ 3.2M 字節,可接受)。回溯時:從目標狀態反向,找到空格位置,嘗試反向移動,檢查前驅狀態的 dist 是否比當前少 1 且已訪問,確定操作字符。
int factorial[10];
void initFact() { factorial[0]=1; for(int i=1;i<10;++i) factorial[i]=factorial[i-1]*i; }
int cantor(int *a) {
int rank = 0;
for (int i = 0; i < 9; ++i) {
int cnt = 0;
for (int j = i+1; j < 9; ++j) if (a[j] < a[i]) ++cnt;
rank += cnt * factorial[8-i];
}
return rank;
}
// BFS 主循環略
// 回溯輸出
void printPath(int startRank, int goalRank) {
if (startRank == goalRank) return;
int cur[9]; memcpy(cur, state[goalRank], sizeof(cur));
int zeroPos = find(cur, cur+9, 0) - cur;
for (int d = 0; d < 4; ++d) {
int nz = zeroPos + dx[d], ny = zeroPos + dy[d]; // 反向移動
if (valid) {
swap(cur[zeroPos], cur[nz]);
int pr = cantor(cur);
if (dist[pr] + 1 == dist[goalRank] && visited[pr]) {
printPath(startRank, pr);
putchar(dirChar[d]); // 對應反向操作
return;
}
swap(cur[zeroPos], cur[nz]); // 復原
}
}
}
visited/dist/state,用 memset 或 vector 重建。goalRank。狀態空間 9! = 362880。BFS 時間空間均 O(9!),C++ 約 0.5s 內完成。
x 代表 0,可能有空格/換行,用 gets 或逐字符讀取。