1077:Eight(八数码问题),考点:广搜状态压缩和去重
原题:http://bailian.openjudge.cn/practice/1077/
描述
The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 x
where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle:
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 6 7 8 5 6 7 8 5 6 7 8 5 6 7 8 9 x 10 12 9 10 x 12 9 10 11 12 9 10 11 12 13 14 11 15 13 14 11 15 13 14 x 15 13 14 15 x r-> d-> r->
The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively.
Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course).
In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three
arrangement.
输入
You will receive a description of a configuration of the 8 puzzle. The description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle
1 2 3 x 4 6 7 5 8
is described by this list:
1 2 3 x 4 6 7 5 8
输出
You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line.
样例输入
2 3 4 1 5 x 7 6 8
样例输出
ullddrurdllurdruldr
解法
思路:广搜,同时记录路径。但是状态要用数字,以及用set来判重,不然会MLE。
版本一:单向广搜,用set判重,自己写并维护一个队列(这也是给出的标准做法)
1 //八数码问题,单向广搜,用set判重,还要记录路径 2 #define _CRT_SECURE_NO_WARNINGS 3 #include <iostream> 4 #include <set> 5 #include <cstring> 6 using namespace std; 7 int goal = 123456780; 8 const int MAXS = 400000;//最多有9!大概360000的状态数 9 struct Node { 10 int status; 11 int parent; 12 char move; 13 Node(){} 14 Node(int s,int p,char m):status(s),parent(p),move(m){} 15 }; 16 //自己写并维护一个队列 17 Node myqueue[MAXS]; 18 int qHead;//队头指针 19 int qTail;//队尾指针 20 //四种移动方式,上下右左 21 char moves[4] = { 'u','d','r','l' }; 22 int getNewStatus(int status, char move) { 23 char temp[20]; 24 int zeroPos; 25 sprintf(temp, "%09d", status); 26 for(int i=0;i<9;i++) 27 if (temp[i] == '0') { 28 zeroPos = i; 29 break; 30 } 31 switch (move) { 32 case 'u': 33 if (zeroPos - 3 < 0) 34 return -1; 35 temp[zeroPos] = temp[zeroPos - 3]; 36 temp[zeroPos - 3] = '0'; 37 break; 38 case 'd': 39 if (zeroPos + 3 > 8) 40 return -1; 41 temp[zeroPos] = temp[zeroPos + 3]; 42 temp[zeroPos + 3] = '0'; 43 break; 44 case 'l': 45 if (zeroPos % 3 == 0) 46 return -1; 47 temp[zeroPos] = temp[zeroPos - 1]; 48 temp[zeroPos - 1] = '0'; 49 break; 50 case 'r': 51 if (zeroPos % 3 == 2) 52 return -1; 53 temp[zeroPos] = temp[zeroPos + 1]; 54 temp[zeroPos + 1] = '0'; 55 } 56 return atoi(temp); 57 } 58 bool BFS(int status) { 59 int newstatus; 60 set<int>expanded; 61 myqueue[qHead] = Node(status, -1, 0); 62 expanded.insert(status); 63 while (qHead != qTail) {//队列不为空 64 status = myqueue[qHead].status; 65 if (status == goal) 66 return true; 67 for (int i = 0; i < 4; i++) {//尝试四种移动策略 68 newstatus = getNewStatus(status, moves[i]); 69 if (newstatus == -1)//不可移,试下一种 70 continue; 71 if (expanded.find(newstatus) != expanded.end())//去重 72 continue; 73 expanded.insert(newstatus); 74 myqueue[qTail++] = Node(newstatus, qHead, moves[i]); 75 } 76 qHead++;//相当于pop 77 } 78 return false; 79 } 80 int main() 81 { 82 char line1[50], line2[20]; 83 char result[MAXS]; 84 //由于输入有空格,所以要用getline 85 while (cin.getline(line1, 48)) { 86 qHead = 0; 87 qTail = 1; 88 int i, j; 89 for (i = 0, j = 0; line1[i]; i++) { 90 if (line1[i] != ' ') { 91 if (line1[i] == 'x') 92 line2[j++] = '0'; 93 else 94 line2[j++] = line1[i]; 95 } 96 } 97 line2[j] = 0;//字符串形式的初始状态 98 if (BFS(atoi(line2))) { 99 int moves = 0; 100 int pos = qHead; 101 while (pos != 0) { 102 result[moves++] = myqueue[pos].move; 103 pos = myqueue[pos].parent; 104 } 105 for (int i = moves - 1; i >= 0; i--) 106 cout << result[i]; 107 cout << endl; 108 } 109 else 110 cout << "unsolvable" << endl; 111 } 112 }
版本二:同版本一,使用set去重,但是使用STL的队列,父元素采用指针指向。队列里的元素也是指针。
1 //八数码问题,单向广搜,用set判重,还要记录路径 2 #define _CRT_SECURE_NO_WARNINGS 3 #include <iostream> 4 #include <set> 5 #include <cstring> 6 #include <queue> 7 using namespace std; 8 int goal = 123456780; 9 const int MAXS = 400000;//最多有9!大概360000的状态数 10 struct Node { 11 int status; 12 char move; 13 Node* parent; 14 Node(int s,char m,Node *p):status(s),parent(p),move(m){} 15 }; 16 //四种移动方式,上下右左 17 char moves[4] = { 'u','d','r','l' }; 18 int getNewStatus(int status, char move) { 19 char temp[20]; 20 int zeroPos; 21 sprintf(temp, "%09d", status); 22 for(int i=0;i<9;i++) 23 if (temp[i] == '0') { 24 zeroPos = i; 25 break; 26 } 27 switch (move) { 28 case 'u': 29 if (zeroPos - 3 < 0) 30 return -1; 31 temp[zeroPos] = temp[zeroPos - 3]; 32 temp[zeroPos - 3] = '0'; 33 break; 34 case 'd': 35 if (zeroPos + 3 > 8) 36 return -1; 37 temp[zeroPos] = temp[zeroPos + 3]; 38 temp[zeroPos + 3] = '0'; 39 break; 40 case 'l': 41 if (zeroPos % 3 == 0) 42 return -1; 43 temp[zeroPos] = temp[zeroPos - 1]; 44 temp[zeroPos - 1] = '0'; 45 break; 46 case 'r': 47 if (zeroPos % 3 == 2) 48 return -1; 49 temp[zeroPos] = temp[zeroPos + 1]; 50 temp[zeroPos + 1] = '0'; 51 } 52 return atoi(temp); 53 } 54 int main() 55 { 56 char line1[50], line2[20]; 57 char result[MAXS]; 58 //由于输入有空格,所以要用getline 59 while (cin.getline(line1, 48)) { 60 int i, j; 61 for (i = 0, j = 0; line1[i]; i++) { 62 if (line1[i] != ' ') { 63 if (line1[i] == 'x') 64 line2[j++] = '0'; 65 else 66 line2[j++] = line1[i]; 67 } 68 } 69 line2[j] = 0;//字符串形式的初始状态 70 int oristatus = atoi(line2); 71 int newstatus; 72 set<int>expanded; 73 bool flag = false; 74 queue<Node*>myqueue; 75 Node *top = new Node(oristatus, -1, NULL); 76 myqueue.push(top); 77 expanded.insert(oristatus); 78 while (!myqueue.empty()) {//队列不为空 79 top = myqueue.front(); 80 int status = top->status; 81 if (status == goal) 82 { 83 flag = true; 84 break; 85 } 86 myqueue.pop(); 87 for (int i = 0; i < 4; i++) {//尝试四种移动策略 88 newstatus = getNewStatus(status, moves[i]); 89 if (newstatus == -1)//不可移,试下一种 90 continue; 91 if (expanded.find(newstatus) != expanded.end())//去重 92 continue; 93 expanded.insert(newstatus); 94 Node* newstat = new Node(newstatus, moves[i], top); 95 myqueue.push(newstat); 96 } 97 } 98 if (flag){ 99 int moves = 0; 100 Node* top = myqueue.front(); 101 102 while (top->status != oristatus) { 103 result[moves++] = top->move; 104 top = top->parent; 105 } 106 for (int i = moves - 1; i >= 0; i--) 107 cout << result[i]; 108 cout << endl; 109 } 110 else 111 cout << "unsolvable" << endl; 112 } 113 }
版本三:双向广搜,老师的代码
1 #include <iostream> 2 #include <bitset> 3 #include <cstring> 4 #include <cstdio> 5 #include <cstdlib> 6 #include <set> 7 #include <algorithm> 8 using namespace std; 9 int goalStatus;//目标状态 10 const int MAXS = 400000; 11 char result[MAXS]; 12 struct Node { 13 int status;//状态 14 int father;//父节点指针,即myqueue的下标 15 char move;//移动方式 16 Node(int s,int f,char m):status(s),father(f),move(m){} 17 Node(){} 18 }; 19 Node myqueue[2][MAXS]; 20 int matchingStatus; 21 int matchingQ; 22 int qHead[2]; 23 int qTail[2]; 24 char fourmoves[] = "udrl"; 25 void IntToString(int n, char *strStatus) 26 { 27 sprintf(strStatus, "%09d", n); 28 } 29 int NewStatus(int status, char cmove) 30 { 31 char tmp[20]; 32 int zeropos; 33 IntToString(status, tmp); 34 for (int i = 0; i < 9; ++i) 35 { 36 if (tmp[i] == '0') { 37 zeropos = i; 38 break; 39 } 40 } 41 switch (cmove) 42 { 43 case 'u': 44 if (zeropos - 3 < 0)return -1; 45 else { 46 tmp[zeropos] = tmp[zeropos - 3]; 47 tmp[zeropos - 3] = '0'; 48 } 49 break; 50 case 'd': 51 if (zeropos + 3 > 8)return -1; 52 else { 53 tmp[zeropos] = tmp[zeropos + 3]; 54 tmp[zeropos + 3] = '0'; 55 } 56 break; 57 case 'l': 58 if (zeropos % 3 == 0)return -1; 59 else { 60 tmp[zeropos] = tmp[zeropos - 1]; 61 tmp[zeropos - 1] = '0'; 62 } 63 break; 64 case 'r': 65 if ((zeropos + 1) % 3 == 0)return -1; 66 else { 67 tmp[zeropos] = tmp[zeropos + 1]; 68 tmp[zeropos + 1] = '0'; 69 } 70 } 71 return atoi(tmp); 72 } 73 inline char reversemove(char c) 74 { 75 switch (c) { 76 case 'u':return 'd'; 77 case 'd':return 'u'; 78 case 'l':return 'r'; 79 case 'r':return 'l'; 80 } 81 return 0; 82 } 83 bool DBfs(int status) 84 { 85 int newStatus; 86 set<int>expanded[2]; 87 for (int i = 0; i < 2; ++i) { 88 qHead[i] = 0; 89 qTail[i] = 1; 90 } 91 myqueue[0][0] = Node(status, -1, 0); 92 expanded[0].insert(status); 93 myqueue[1][0] = Node(goalStatus, -1, 0); 94 expanded[1].insert(goalStatus); 95 while (qHead[0] != qTail[0] && qHead[1] != qTail[1]) 96 { 97 int qNo; 98 if (qHead[0] == qTail[0]) 99 qNo = 1; 100 else if (qHead[1] == qTail[1]) 101 qNo = 0; 102 else { 103 if (qTail[0] - qHead[0] < qTail[1] - qHead[1]) 104 qNo = 0; 105 else qNo = 1; 106 } 107 int vqNo = 1 - qNo; 108 status = myqueue[qNo][qHead[qNo]].status; 109 if (expanded[vqNo].find(status) != expanded[vqNo].end()) { 110 matchingStatus = status; 111 matchingQ = qNo; 112 return true; 113 } 114 else { 115 for (int i = 0; i < 4; ++i) 116 { 117 newStatus = NewStatus(status, fourmoves[i]); 118 if (newStatus == -1)continue; 119 if (expanded[qNo].find(newStatus) != expanded[qNo].end())continue; 120 expanded[qNo].insert(newStatus); 121 myqueue[qNo][qTail[qNo]] = Node(newStatus, qHead[qNo], fourmoves[i]); 122 qTail[qNo]++; 123 } 124 qHead[qNo]++; 125 } 126 } 127 return false; 128 } 129 int main() 130 { 131 char line1[50]; 132 char line2[20]; 133 while (cin.getline(line1, 48)) { 134 int i, j; 135 for (i = 0, j = 0; line1[i]; i++) { 136 if (line1[i] != ' ') { 137 if (line1[i] == 'x')line2[j++] = '0'; 138 else line2[j++] = line1[i]; 139 } 140 } 141 line2[j] = '\0'; 142 //用奇偶性判定是否有解 143 int sumGoal = 0; 144 for (int i = 0; i < 8; ++i) 145 sumGoal += (i - 1); 146 int sumStart = 0; 147 for (int i = 0; i < 9; ++i) { 148 if (line2[i] == '0') 149 continue; 150 for (int j = 0; j < i; ++j) { 151 if (line2[j] < line2[i] && line2[j] != '0') 152 sumStart++; 153 } 154 } 155 if (sumStart % 2 != sumGoal % 2) { 156 cout << "unsolveable" << endl; 157 continue; 158 } 159 goalStatus = atoi("123456780"); 160 if (DBfs(atoi(line2))) { 161 int moves = 0; 162 int pos; 163 if (matchingQ == 0)pos = qHead[0]; 164 else { 165 for(int i=0;i<qTail[0];++i) 166 if (myqueue[0][i].status == matchingStatus) { 167 pos = i; 168 break; 169 } 170 } 171 do { 172 if (pos) { 173 result[moves++] = myqueue[0][pos].move; 174 pos = myqueue[0][pos].father; 175 } 176 } while (pos); 177 reverse(result, result + moves); 178 if (matchingQ == 0) { 179 for(int i=0;i<qTail[1];++i) 180 if (myqueue[1][i].status == matchingStatus) { 181 pos = i; 182 break; 183 } 184 } 185 else pos = qHead[1]; 186 do { 187 if (pos) { 188 result[moves++] = reversemove(myqueue[1][pos].move); 189 pos = myqueue[1][pos].father; 190 } 191 } while (pos); 192 for (int i = 0; i < moves; ++i) 193 cout << result[i]; 194 cout << endl; 195 } 196 else cout << "unsolvalbe" << endl; 197 } 198 return 0; 199 }