2007百度之星總決賽試題八方塊移動的簡單演算法
阿新 • • 發佈:2019-02-20
八方塊移動遊戲要求從一個含8個數字(用1-8表示)的方塊以及一個空格方塊(用0表示)的3x3矩陣的起始狀態開始,
不斷移動該空格方塊以使其和相鄰的方塊互換,直至達到所定義的目標狀態。
空格方塊在中間位置時有上、下、左、右4個方向可移動,在四個角落上有2個方向可移動,在其它位置上有3個方向可移動。
例如,假設一個3x3矩陣的初始狀態為:
8 0 3
2 1 4
7 6 5
要求的目標狀態為:
1 2 3
8 0 4
7 6 5
則一個合法的移動路徑為:
8 0 3 8 1 3 8 1 3 0 1 3 1 0 3 1 2 3
2 1 4 => 2 0 4 => 0 2 4 => 8 2 4 => 8 2 4 => 8 0 4
7 6 5 7 6 5 7 6 5 7 6 5 7 6 5 7 6 5
另外,在所用可能的從初始狀態到目標狀態的移動路徑中,步數最少的路徑被稱為最短路徑;
在上面的例子中,最短路徑為5。如果不存在從初始狀態到目標狀態的任何路徑,則稱該組狀態無解。
請設計演算法找到從八方塊的某初始狀態到某目標狀態的所有可能路徑的最短路徑
- #define WIN32_LEAN_AND_MEAN
- #define _CRT_SECURE_NO_DEPRECATE
- #include <iostream>
- #include <map>
- #include <queue>
- #include <cstring>
- #include <ctime>
- #include <cassert>
- const unsigned int strLength = 9;
- usingnamespace std;
- // 交換字串src中 x和y的值,返回被交換後的新串
- char* swap(char* src, int x, int y)
- {
- assert(strlen(src)==strLength);
- char* dest = newchar[strLength+1];
- strcpy(dest, src);
- char temp = dest[x];
- dest[x] = dest[y];
- dest[y] = temp;
- return dest;
- }
- //為Map<char*,char*>提供的比較函式.用於比較字串值,取代預設的比較字串地址
- struct ltstr
- {
- bool operator()(constchar* s1, constchar* s2) const
- {
- return strcmp(s1, s2) < 0;
- }
- };
- //核心演算法,start為初始狀態 goal為目標狀態
- //沒有使用A* 演算法 或啟發式尋找 我在這個題目中,我找不到這樣的啟發條件.
- //我覺的啟發式並不一定能得到最優解 這題明確要求是最短路徑
- //用隊來實現廣度優先 用map來儲存所有節點.實現圖的效果 map中每項資料由<節點,節點的父節點>構成
- //返回值為兩種狀態需要的最短步數.
- int FindStep(char* start, char* goal)
- {
- assert(strlen(start)==strLength);
- assert(strlen(goal)==strLength);
- map<char*, char*, ltstr> strMap;
- map<char*, char*,ltstr>::iterator iter;
- queue<char*> strQueue;
- strQueue.push(start);
- unsigned int step = 0;
- while (!strQueue.empty())
- {
- char* str = strQueue.front();
- strQueue.pop();
- //確定節點中字元'0'所在的位置
- unsigned int pos;
- for (pos = 0; pos < strLength && str[pos] != '0'; pos++);
- //由當前結點移位得到4個新的子結點的指標陣列初始為NULL
- char* generate[4];
- for (int i = 0; i < 4; i++)
- generate[i] = NULL;
- //當前節點左移 右移 上移 下移 並生成新節點
- if ((pos % 3) != 0) generate[0] = swap(str, pos, pos - 1);
- if ((pos % 3) != 2) generate[1] = swap(str, pos, pos + 1);
- if (pos > 2) generate[2] = swap(str, pos, pos - 3);
- if (pos < 6) generate[3] = swap(str, pos, pos + 3);
- //處理生成的四個新節點
- for (int i = 0; i < 4; i++)
- {
- if (generate[i] == NULL)
- continue;
- //如果此結點圖中(Map中)已存在,刪除此結點 並返回
- if (strMap.find(generate[i]) != strMap.end())
- {
- delete generate[i];
- continue;
- }
- //如果此結點不是目標狀態節點則入隊,並將此節點加入圖中
- if (strcmp(generate[i], goal) != 0)
- {
- strQueue.push(generate[i]);
- strMap.insert(make_pair(generate[i], str));
- continue;
- }
- strMap.insert(make_pair(generate[i], str));
- char *back = generate[i];
- //執行到這裡則說明此結點為目標狀態結點遁環查詢其父結點
- while (strcmp(back, start) != 0)
- {
- step++;
- back = (strMap.find(back))->second;
- cout << back << "/n";
- }
- //釋放記憶體 並返回步數;
- for (iter = strMap.begin(); iter != strMap.end(); iter++)
- delete iter->first;
- return step;
- }
- }
- //目標狀態不可到達 釋放記憶體 返回-1;
- for (iter = strMap.begin(); iter != strMap.end(); iter++)
- delete iter->first;
- return -1;
- }
- int main()
- {
- //char* start = "803214765";
- char* start = "784356102";
- char* goal = "123804765";
- //char* goal = "012345678";
- cout << "開始尋找..." << endl;
- clock_t startTime = clock();
- cout << "需要 " << FindStep(start, goal) << " 步.(如果為-1則目標狀態不可到達) /n";
- cout << "共用時" << (clock() - startTime) << "毫秒.(關閉FindStep函式中的cout輸出可以更快點) "<<endl;
- system("PAUSE");
- return 0;
- }