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 }

 

 

 

posted @ 2021-07-14 15:04  永远是个小孩子  阅读(157)  评论(0编辑  收藏  举报