On a 2x3 board
, there are 5 tiles represented by the integers 1 through 5, and an empty square represented by 0.html
A move consists of choosing 0
and a 4-directionally adjacent number and swapping it.數組
The state of the board is solved if and only if the board
is [[1,2,3],[4,5,0]].
app
Given a puzzle board, return the least number of moves required so that the state of the board is solved. If it is impossible for the state of the board to be solved, return -1.post
Examples:學習
Input: board = [[1,2,3],[4,0,5]] Output: 1 Explanation: Swap the 0 and the 5 in one move.
Input: board = [[1,2,3],[5,4,0]] Output: -1 Explanation: No number of moves will make the board solved.
Input: board = [[4,1,2],[5,0,3]] Output: 5 Explanation: 5 is the smallest number of moves that solves the board. An example path: After move 0: [[4,1,2],[5,0,3]] After move 1: [[4,1,2],[0,5,3]] After move 2: [[0,1,2],[4,5,3]] After move 3: [[1,0,2],[4,5,3]] After move 4: [[1,2,0],[4,5,3]] After move 5: [[1,2,3],[4,5,0]]
Input: board = [[3,2,4],[1,5,0]] Output: 14
Note:ui
board
will be a 2 x 3 array as described above.board[i][j]
will be a permutation of [0, 1, 2, 3, 4, 5]
.
看到這道題不由讓博主想起了文曲星上的遊戲-華容道,好吧,又暴露年齡了|||-.-,貌似文曲星這種電子辭典神馬的已是很古老的東西了,可是上面的一些經典遊戲,什麼英雄壇說啊,華容道啊,雖然像素分辨率低的可憐,畫面效果連小霸王學習機其樂無窮都比不上,更不要說跟如今的什麼擼啊擼,吃雞之類的畫面相比了,可是卻給初高中時代的博主學習之餘帶來了無限的樂趣。不過這題跟華容道仍是有些不一樣的,由於那個遊戲各塊的大小不一樣,而這道題的拼圖大小都是同樣的。那麼像這種相似迷宮遍歷的問題,又要求最小值的問題,要有強烈的BFS的感受,沒錯,這道題正是用BFS來解的。這道題好就好在限定了棋盤的大小,是2x3的,這就讓題目簡單了許多,因爲0的位置只有6個,咱們能夠列舉出全部其下一步可能移動到的位置。爲了知道每次移動後拼圖是否已經恢復了正確的位置,咱們確定須要用個常量表示出正確位置以做爲比較,那麼對於這個正確的位置,咱們還用二維數組表示嗎?也不是不行,但咱們能夠更加簡潔一些,就用一個字符串 "123450"來表示就好了,注意這裏咱們是把第二行直接拼接到第一行後面的,數字3和4起始並非相連的。好,下面來看0在不一樣位置上能去的地方,字符串長度爲6,則其座標爲 012345,轉回二維數組爲:url
0 1 2 3 4 5
那麼當0在位置0時,其能夠移動到右邊和下邊,即{1, 3}位置;在位置1時,其能夠移動到左邊,右邊和下邊,即{0, 2, 4}位置;在位置2時,其能夠移動到左邊和下邊,即{1, 5}位置;在位置3時,其能夠移動到上邊和右邊,即{0, 4}位置;在位置4時,其能夠移動到左邊,右邊和上邊,即{1, 3, 5}位置;在位置5時,其能夠移動到上邊和左邊,即{2, 4}位置。spa
而後就是標準的BFS的流程了,使用一個HashSet來記錄訪問過的狀態,將初始狀態start放入,使用一個queue開始遍歷,將初始狀態start放入。而後就是按層遍歷,取出隊首狀態,先和target比較,相同就直接返回步數,不然就找出當前狀態中0的位置,到dirs中去找下一個能去的位置,賦值一個臨時變量cand,去交換0和其下一個位置,生成一個新的狀態,若是這個狀態不在visited中,則加入visited,而且壓入隊列queue,步數自增1。若是while循環退出後都沒有回到正確狀態,則返回-1,參見代碼以下:code
解法一:htm
class Solution { public: int slidingPuzzle(vector<vector<int>>& board) { int res = 0, m = board.size(), n = board[0].size(); string target = "123450", start = ""; vector<vector<int>> dirs{{1,3}, {0,2,4}, {1,5}, {0,4}, {1,3,5}, {2,4}}; for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { start += to_string(board[i][j]); } } unordered_set<string> visited{start}; queue<string> q{{start}}; while (!q.empty()) { for (int i = q.size() - 1; i >= 0; --i) { string cur = q.front(); q.pop(); if (cur == target) return res; int zero_idx = cur.find("0"); for (int dir : dirs[zero_idx]) { string cand = cur; swap(cand[dir], cand[zero_idx]); if (visited.count(cand)) continue; visited.insert(cand); q.push(cand); } } ++res; } return -1; } };
上面的解法雖然很炫,可是有侷限性,好比若棋盤很大的話,難道咱們還手動列出全部0能去的位置麼?其實咱們可使用最普通的BFS遍歷方式,就檢查上下左右四個方向,那麼這樣咱們就不能壓縮二維數組成一個字符串了,咱們visited數組中只能放二維數組了,一樣的,queue 中也只能放二維數組,因爲二維數組要找0的位置的話,還須要遍歷,爲了節省遍歷時間,咱們將0的位置也放入queue中,那麼queue中的放的就是一個pair對兒,保存當前狀態,已經0的位置,初始時將棋盤以及0的位置排入queue中。以後的操做就跟以前的解法沒啥區別了,只不過這裏咱們的心位置就是上下左右,若是未越界的話,那麼和0交換位置,看新狀態是否已經出現過,若是這個狀態不在visited中,則加入visited,而且壓入隊列queue,步數自增1。若是while循環退出後都沒有回到正確狀態,則返回-1,參見代碼以下:
解法二:
class Solution { public: int slidingPuzzle(vector<vector<int>>& board) { int res = 0; set<vector<vector<int>>> visited; queue<pair<vector<vector<int>>, vector<int>>> q; vector<vector<int>> correct{{1, 2, 3}, {4, 5, 0}}; vector<vector<int>> dirs{{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; for (int i = 0; i < 2; ++i) { for (int j = 0; j < 3; ++j) { if (board[i][j] == 0) q.push({board, {i, j}}); } } while (!q.empty()) { for (int i = q.size() - 1; i >= 0; --i) { auto t = q.front().first; auto zero = q.front().second; q.pop(); if (t == correct) return res; visited.insert(t); for (auto dir : dirs) { int x = zero[0] + dir[0], y = zero[1] + dir[1]; if (x < 0 || x >= 2 || y < 0 || y >= 3) continue; vector<vector<int>> cand = t; swap(cand[zero[0]][zero[1]], cand[x][y]); if (visited.count(cand)) continue; q.push({cand, {x, y}}); } } ++res; } return -1; } };
參考資料:
https://leetcode.com/problems/sliding-puzzle/discuss/113613/C++-9-lines-DFS-and-BFS