1. 程式人生 > >HDU_1043 Eight 【逆向BFS + 康託展開 】【A* + 康託展開 】

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程式碼