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 }
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 }