BFS 經典題,可能答案不是最優的,參考一下也是可以的 ^ _ ^
題目:
你玩過華容道的遊戲嗎?
這是個類似的,但更簡單的遊戲。
看下面 3 x 2 的格子
+---+---+---+
| A | * | * |
+---+---+---+
| B | | * |
+---+---+---+
在其中放5張牌,其中A代表關羽,B代表張飛, * 代表士兵。
還有一個格子是空着的。
你可以把一張牌移動到相鄰的空格中去(對角不算相鄰)。
遊戲的目標是:關羽和張飛 交換位置
,其它的牌隨便在哪裏都可以
。
輸入格式:
輸入兩行6個字符表示當前的局面
輸出格式:
一個整數,表示最少多少步,才能把AB換位(其它牌位置隨意)
例如,輸入:
* A
* * B
程序應該輸出:
17
再例如,輸入:
A B
* * *
程序應該輸出:
12
這個題目剛開始是使用 DFS做的,但是最後答案一直不對,隨後想了一下,發現 DFS是根本行不通的,因爲 DFS比較深度,將 A,B互換位置的次數不可能是最少的,大家可以畫個圖想一想 ^ _ ^
比如下面的代碼:
這是使用深搜思想(DFS)寫的,我們測試過的數據如下:
我們發現最少的情況也有 36步,但其實,這個地圖我們只需要 12步就可以完成 關羽 和 張飛的互換 . . .
而我們使用的最好解是使用 ——> BFS(廣度優先搜索)
BFS 是將當前最近的一個數據進行處理,大家可以動手畫一畫,爲什麼是使用 BFS是最好的 . . .
下面我將詳細的講解 BFS求解 《卡片換位》 . . .
.
BFS 思想求解
題目思想:
- 定位當前空格的所在位置,向四個方向一直探索
- 將空格與探索到的位置上的數據 互換
- 獲取更新後的地圖,並且與之前有過的地圖樣子相比,如若相同,則此路不行!進行下一個方向的探索
- 實時判斷是否已經將 關羽 和 張飛互換位置了 . . .
關鍵點解析:
-
首先,準備一個類,用於標識當前的狀態(空格位置、地圖樣子、空格移動步數):
class Node { public: int currX; // 當前空格的所在位置 int currY; string currStr; // 當前的字符串(地圖情景) int currStep; // 當前移動了多少步 };
-
方向準備(上、下、左、右):
// 下一個方向 int nextDirection[4][2]{ { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 } };
-
準備一個隊列和集合容器(用於存放空格位置,判斷地圖是否重複):
// 存放當前的一些數據(空格的位置、地圖、步數) queue<Node> que; // 判斷當前的地圖是否在以前存在過了(避免循環移位) set<string> judgeIsNoRepetition;
-
探索四個方向,並且判斷是否出界:
for (size_t i = 0; i < 4; i++) { // 獲取下一步的位置 int cx = curr.currX + nextDirection[i][0]; int cy = curr.currY + nextDirection[i][1]; }
-
地圖更新,並使用類封裝新的相關數據:
// 空格到其它位置 產生的新的地圖 string str = curr.currStr; // 交換位置後,產生新的地圖 swap(str[curr.currX * 3 + curr.currY], str[cx * 3 + cy]); Node tmp; // 當前最新生成的數據 tmp.currX = cx; tmp.currY = cy; tmp.currStr = str; tmp.currStep = curr.currStep + 1; // 上一步 + 1
-
判斷地圖是否存在過,避免重複:
// 判斷這個地圖是否重複過 ! ! ! if (judgeIsNoRepetition.count(tmp.currStr) != 0) continue; judgeIsNoRepetition.insert(tmp.currStr); // 加入到集合這中 que.push(tmp); // 入隊,繼續進行下一步同樣的操作
重要的步驟就上面的這些,下面是完整的代碼,大家看懂之後,可以自己寫一遍,^ _ ^
代碼和圖片的順序一樣
.