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