HDU_1043 Eight 【逆向BFS + 康托展开 】【A* + 康托展开 】

一、题目

http://acm.hdu.edu.cn/showproblem.php?pid=1043

二、两种方法

该题很明显,是一个八数码的问题,就是9宫格,里面有一个空格,外加1~8的数字,任意一种情况,如果能通过移动空格使数码组成

1 2 3
4 5 6
7 8 0 

的形式,就输出变换的序列,如果不能,输出unsolvable.

逆向$BFS$+康托展开

1.什么是康托展开

https://zh.wikipedia.org/wiki/%E5%BA%B7%E6%89%98%E5%B1%95%E5%BC%80

$wiki$讲解比较好。

有了康托展开,你可能会有疑问,要它有何用?但如果你联系一下$hash$函数,康托展开能够保证每一种八数码的情况都能够用一个整型数字唯一表示,这样是不是就好理解了?

2.逆向BFS

为了求出它的变换序列,我们可以逆向思维想一下,如果我从最终结果出发去变换,然后把路途中的每一种情况都记录下来(有了康托展开对八数码进行$hash$,会很方便),记录变换路径,然后对输入的其实条件直接进行输出就可以了。

这就是逆向$BFS$的解决方案。

这里需要注意的是,如果用STL的queue以及string,可能会造成超内存。解决办法就是用个数组模拟即可。

  1 #include <vector>
  2 #include <cstdio>
  3 #include <iostream>
  4 #include <fstream>
  5 #include <queue>
  6 #include <cstring>
  7 
  8 using namespace std;
  9 
 10 const int MAXN = 370000;
 11 const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};    //factorial
 12 const int dx[] = {-1, 1, 0, 0};
 13 const int dy[] = {0, 0, 1, -1};
 14 const char op[] = "dulr";   //operation
 15 vector<char> Path[MAXN];          //记录路径
 16 bool Visit[MAXN];           //标记数组
 17 struct Node
 18 {
 19     int S[9];       //二维的数码表一维表示(下标从1开始)
 20     int loc;       //9 = x  loaction
 21     int cat;        //对应的康拓展开值
 22 };
 23 Node Q[MAXN];
 24 int Cantor(int s[])
 25 {
 26     int t, ans = 1;
 27     for(int i = 0; i < 9; i++)
 28     {
 29         t = 0;
 30         for(int j = i+1; j < 9; j++)
 31         {
 32             if(s[j] < s[i])
 33                 t++;
 34         }
 35         ans += t*fac[9-i-1];
 36     }
 37     return ans;
 38 }
 39 
 40 
 41 void BFS()
 42 {
 43     memset(Visit, 0, sizeof(Visit));
 44     int x, y, Cnt = 0, Rea = 0;
 45     Node cur, t;
 46     for(int i = 0; i < 8; i++)
 47         cur.S[i] = i+1;
 48     cur.S[8] = 0;
 49     cur.loc = 8;
 50     cur.cat = Cantor(cur.S);
 51     //Path[cur.cat] = "";
 52     Visit[cur.cat] = 1;
 53     Q[Cnt++] = cur;
 54     
 55     while(Rea < Cnt)
 56     {
 57         
 58         t = Q[Rea];
 59         Rea++;
 60         for(int i = 0; i < 4; i++)
 61         {
 62             x = t.loc/3 + dx[i];
 63             y = t.loc%3 + dy[i]; 
 64             if(x < 0 || x > 2 || y < 0 || y > 2)
 65                 continue;
 66             cur = t;                        //**
 67             cur.loc = x*3+y;
 68             cur.S[t.loc] = t.S[cur.loc];    //交换
 69             cur.S[cur.loc] = 0;             //X
 70             cur.cat = Cantor(cur.S);
 71             if(!Visit[cur.cat])
 72             {
 73                 Visit[cur.cat] = 1;
 74                 Path[cur.cat] = Path[t.cat];
 75                 Path[cur.cat].push_back(op[i]);
 76                 //Path[cur.cat] = op[i] + Path[t.cat];
 77                 Q[Cnt++] = cur;
 78 
 79             }
 80         }
 81     }
 82 }
 83 
 84 
 85 int main()
 86 {
 87     //freopen("input.txt", "r", stdin);
 88     //freopen("out.txt", "w", stdout);
 89     int s[10];
 90     char c[2];
 91     BFS();
 92 
 93 
 94     while(scanf("%s", c)!=EOF)
 95     {
 96         if(c[0] == 'x')
 97             s[0] = 0;
 98         else
 99             s[0] = c[0] - '0';
100         for(int i = 1; i < 9; i++)
101         {
102             scanf("%s", c);
103             if(c[0] == 'x')
104                 s[i] = 0;
105             else
106                 s[i] = c[0] - '0';
107         }
108         int Cat = Cantor(s);
109         if(Visit[Cat])
110          {   
111             for(int i = Path[Cat].size()-1; i >= 0; i--)
112                 printf("%c", Path[Cat][i]);
113             printf("\n");
114          }
115         else
116             printf("unsolvable\n");//cout << "unsolvable" << endl;
117     }
118     return 0;
119 }
AC代码

3.A*

http://www.cnblogs.com/me-sa/archive/2010/05/18/A-Star-Pathfinding-for-Beginners.html

基本A*算法的入门讲解都是这个。其实当你认真体会后,A*算法的关键就是F=G+H中的G,H如何算的问题,其他的与搜索大同小异,因为有了G,H,就可以有目的性的去搜索,也就是启发式搜索。(仅个人理解)

这个题目里,求H依然采用的是曼哈顿距离,即每个每个数从当前位置到它最终位置的曼哈顿距离。

这里,还用到了小技巧,就是逆序数,当在满足上述约定的八数码问题中,空格与相邻棋子的交换不会改变棋局中棋子数列的逆序数的奇偶性。因为最终情况的逆序数是偶数,所以要保证每次搜索过程中逆序数都是偶数。这样就达到了剪枝。

需要注意的是,求逆序数,无论空格是代表的0还是9,都不要考虑进去。

推广一下:对于N*M数码问题,空白在同一行交换不会导致奇偶性互变;上下行交换,如果列为奇数,则不会导致奇偶性互变;如果列数为偶数,则会导致奇偶性互变,所以此时还要考虑上下行交换的次数,综合得出答案。

 

  1 /*逆向BFS*/
  2 /*
  3 #include <vector>
  4 #include <cstdio>
  5 #include <iostream>
  6 #include <fstream>
  7 #include <queue>
  8 #include <cstring>
  9 
 10 using namespace std;
 11 
 12 const int MAXN = 370000;
 13 const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};    //factorial
 14 const int dx[] = {-1, 1, 0, 0};
 15 const int dy[] = {0, 0, 1, -1};
 16 const char op[] = "dulr";   //operation
 17 vector<char> Path[MAXN];          //记录路径
 18 bool Visit[MAXN];           //标记数组
 19 struct Node
 20 {
 21     int S[9];       //二维的数码表一维表示(下标从1开始)
 22     int loc;       //9 = x  loaction
 23     int cat;        //对应的康拓展开值
 24 };
 25 Node Q[MAXN];
 26 int Cantor(int s[])
 27 {
 28     int t, ans = 1;
 29     for(int i = 0; i < 9; i++)
 30     {
 31         t = 0;
 32         for(int j = i+1; j < 9; j++)
 33         {
 34             if(s[j] < s[i])
 35                 t++;
 36         }
 37         ans += t*fac[9-i-1];
 38     }
 39     return ans;
 40 }
 41 
 42 
 43 void BFS()
 44 {
 45     memset(Visit, 0, sizeof(Visit));
 46     int x, y, Cnt = 0, Rea = 0;
 47     Node cur, t;
 48     for(int i = 0; i < 8; i++)
 49         cur.S[i] = i+1;
 50     cur.S[8] = 0;
 51     cur.loc = 8;
 52     cur.cat = Cantor(cur.S);
 53     //Path[cur.cat] = "";
 54     Visit[cur.cat] = 1;
 55     Q[Cnt++] = cur;
 56     
 57     while(Rea < Cnt)
 58     {
 59         
 60         t = Q[Rea];
 61         Rea++;
 62         for(int i = 0; i < 4; i++)
 63         {
 64             x = t.loc/3 + dx[i];
 65             y = t.loc%3 + dy[i]; 
 66             if(x < 0 || x > 2 || y < 0 || y > 2)
 67                 continue;
 68             cur = t;                        //**
 69             cur.loc = x*3+y;
 70             cur.S[t.loc] = t.S[cur.loc];    //交换
 71             cur.S[cur.loc] = 0;             //X
 72             cur.cat = Cantor(cur.S);
 73             if(!Visit[cur.cat])
 74             {
 75                 Visit[cur.cat] = 1;
 76                 Path[cur.cat] = Path[t.cat];
 77                 Path[cur.cat].push_back(op[i]);
 78                 //Path[cur.cat] = op[i] + Path[t.cat];
 79                 Q[Cnt++] = cur;
 80 
 81             }
 82         }
 83     }
 84 }
 85 
 86 
 87 int main()
 88 {
 89     //freopen("input.txt", "r", stdin);
 90     //freopen("out.txt", "w", stdout);
 91     int s[10];
 92     char c[2];
 93     BFS();
 94 
 95 
 96     while(scanf("%s", c)!=EOF)
 97     {
 98         if(c[0] == 'x')
 99             s[0] = 0;
100         else
101             s[0] = c[0] - '0';
102         for(int i = 1; i < 9; i++)
103         {
104             scanf("%s", c);
105             if(c[0] == 'x')
106                 s[i] = 0;
107             else
108                 s[i] = c[0] - '0';
109         }
110         int Cat = Cantor(s);
111         if(Visit[Cat])
112          {   
113             for(int i = Path[Cat].size()-1; i >= 0; i--)
114                 printf("%c", Path[Cat][i]);
115             printf("\n");
116          }
117         else
118             printf("unsolvable\n");//cout << "unsolvable" << endl;
119     }
120     return 0;
121 }
122 2 3 4 1 5 0 7 6 8 
123 2 3 0 1 5 4 7 6 8 
124 90747
125 2 3 4 1 5 0 7 6 8 
126 92307
127 
128 */
129 
130 /* A* */
131 
132 #include <cstdio>
133 #include <iostream>
134 #include <cstring>
135 #include <queue>
136 #include <fstream>
137 
138 using namespace std;
139 
140 const int MAXN = 4000000;
141 const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};    //factorial
142 const int dx[] = {0, 0, 1, -1};
143 const int dy[] = {1, -1, 0, 0};
144 const char op[] = "rldu";   //operation
145 const int Aim = 46234;
146 int Pre[MAXN];
147 char Vp[MAXN];
148 bool Visit[MAXN];
149 struct Node
150 {
151     int s[9];
152     int cat;
153     int g, h;
154     int loc;
155 
156     bool operator < (const Node &t)const
157     {
158         if(h == t.h)
159             return g > t.g;
160         return h > t.h;
161     }
162 };
163 
164 int getCator(const int s[])
165 {
166     int t, ans = 1;
167     for(int i = 0; i < 9; i++)
168     {
169         t = 0;
170         for(int j = i+1; j < 9; j++)
171         {
172             if(s[j] < s[i])
173                 t++;
174         }
175         ans += t*fac[9-i-1];
176     }
177     return ans;
178     
179 }
180 
181 int getH(const int s[])
182 {
183     int x, y, x0, y0, ans = 0;
184     for(int i = 0; i < 9; i++)
185     {
186         if(s[i])
187         {
188             x0 = i/3, y0 = i%3;
189             x = (s[i]-1)/3, y = (s[i]-1)%3;     //这里要注意
190             ans += abs(x-x0) + abs(y-y0);       //曼哈顿距离
191         }
192     }
193     return ans;
194 }
195 
196 bool judge(const int S[])       //判断逆序对数是否为偶数
197 {
198     int ans = 0;
199     for(int i = 0; i < 9; i++)
200     {
201         for(int j = i+1; j < 9; j++)
202         {
203             if( S[j] && S[i] && S[j] < S[i] )
204                 ans++;
205         }
206     }
207     if(ans%2 == 0)
208         return true;
209     else 
210         return false;
211 }
212 
213 void astar(Node cur)
214 {
215     memset(Visit, 0, sizeof(Visit));
216     memset(Pre, -1, sizeof(Pre));
217     int x, y;
218     priority_queue<Node> PQ;
219     PQ.push(cur);
220     Visit[cur.cat] = 1;
221     Pre[cur.cat] = -1;
222 
223     while(!PQ.empty())
224     {
225         Node t = PQ.top();
226         PQ.pop();
227         for(int i = 0; i < 4; i++)
228         {
229             x = t.loc/3 + dx[i];
230             y = t.loc%3 + dy[i];
231             if(x < 0 || x > 2 || y < 0 || y > 2)
232                 continue;
233             cur = t;
234             cur.loc = x*3 + y;
235             cur.s[t.loc] = t.s[cur.loc];
236             cur.s[cur.loc] = 0;
237             cur.cat = getCator(cur.s);
238 
239             if(Visit[cur.cat] == 0 && judge(cur.s))
240             {
241 
242                 Visit[cur.cat] = 1;
243                 cur.h = getH(cur.s);
244                 cur.g++;
245                 Pre[cur.cat] = t.cat;
246                 Vp[cur.cat] = op[i];
247                 if(cur.cat == Aim)
248                     return;
249                 PQ.push(cur);
250             }
251         }
252     }
253 }
254 
255 void Print()
256 {
257     int c = Aim;
258     string ans = "";
259     while(Pre[c] != -1)
260     {
261         ans = Vp[c]+ans;
262         c = Pre[c];
263     }
264     cout << ans << endl;
265 }
266 
267 
268 int main()
269 {
270     //freopen("input.txt", "r", stdin);
271     //freopen("out.txt", "w", stdout);
272     Node cur;
273     char c[2];
274     while(scanf("%s", c)!=EOF)
275     {
276         if(c[0] == 'x')
277         {
278             cur.s[0] = 0;
279             cur.loc = 0;
280         }
281         else
282             cur.s[0] = c[0] - '0';
283         for(int i = 1; i < 9; i++)
284         {
285             scanf("%s", c);
286             if(c[0] == 'x')
287             {
288                 cur.s[i] = 0;
289                 cur.loc = i;
290             }
291             else
292                 cur.s[i] = c[0] - '0';
293         }
294 
295         if(!judge(cur.s))
296         {
297             printf("unsolvable\n");
298             continue;
299         }
300         cur.cat = getCator(cur.s);
301         cur.g = 0, cur.h = getH(cur.s);
302         astar(cur);
303         Print();
304     }
305     return 0;
306 }
AC代码
posted @ 2018-12-03 20:46  Dybala21  阅读(191)  评论(0编辑  收藏  举报