八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
自己看了两天把各种思路的代码敲了下,题目是:POJ - 1077 Eight(松松可以过的) HDU - 1043 Eight(多组输入,容易卡内存,打表也能过)
//境界一:BFS+STL #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<set> #include<queue> #include<stack> #include<map> using namespace std; string g; set<string>vis; queue<string>que; struct node { string pre; char op; }; map<string, node>road; bool moveUp(string &g) { int posx; for (int i = 0; i < 9; i++) { if (g[i] == 'x') { posx = i; break; } } if (posx <= 2) return false; else { swap(g[posx], g[posx - 3]); return true; } } bool moveDown(string &g) { int posx; for (int i = 0; i < 9; i++) { if (g[i] == 'x') { posx = i; break; } } if (posx >= 6) return false; else { swap(g[posx], g[posx + 3]); return true; } } bool moveLeft(string &g) { int posx; for (int i = 0; i < 9; i++) { if (g[i] == 'x') { posx = i; break; } } if (posx == 0 || posx==3 || posx==6) return false; else { swap(g[posx], g[posx - 1]); return true; } } bool moveRight(string &g) { int posx; for (int i = 0; i < 9; i++) { if (g[i] == 'x') { posx = i; break; } } if (posx == 2 || posx==5 ||posx==8 ) return false; else { swap(g[posx], g[posx + 1]); return true; } } int bfs() { while (!que.empty()) que.pop(); vis.clear(); road.clear(); que.push(g); vis.insert(g); while (!que.empty()) { string loc = que.front(); que.pop(); if (loc == "12345678x") return true; for (int i = 0; i < 4; i++) { string noc = loc; if (i == 0) { if (moveUp(noc) && vis.count(noc)==0) { que.push(noc); vis.insert(noc); road[noc].pre = loc; road[noc].op = 'u'; } } else if (i == 1) { if (moveDown(noc) && vis.count(noc) == 0) { que.push(noc); vis.insert(noc); road[noc].pre = loc; road[noc].op = 'd'; } } else if (i == 2) { if (moveLeft(noc) && vis.count(noc) == 0) { que.push(noc); vis.insert(noc); road[noc].pre = loc; road[noc].op = 'l'; } } else if (i == 3) { if (moveRight(noc) && vis.count(noc) == 0) { que.push(noc); vis.insert(noc); road[noc].pre = loc; road[noc].op = 'r'; } } } } return -1; } int main() { char c; for (int i = 0; i < 9; i++) { cin >> c; g += c; } int ans=bfs(); if (ans == -1) cout << "unsolvable" << endl; else { string np = "12345678x"; stack<char>sop; while (np != g) { sop.push(road[np].op); np = road[np].pre; } while (!sop.empty()) { cout << sop.top(); sop.pop(); } cout << endl; } }
//境界二:BFS+康托展开 #include<iostream> #include<cstdio> #include<cstring> #include<stack> using namespace std; typedef int State[9]; const int maxn = 1000000; State st[maxn], goal; //状态数组,用来实现队列 int dis[maxn]; //距离数组 int fa[maxn]; //记录前一个状态的编号 char curop[maxn]; //记录达到该状态的对应操作 bool vis[maxn]; //结点是否访问过 const int dirx[] = { -1, 1, 0, 0 }; const int diry[] = { 0, 0, -1, 1 }; const char cop[] = { 'u', 'd', 'l', 'r' }; const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; //康托展开 int cantor(State s) { int ans = 0; for (int i = 0; i<9; i++) { int tmp = 0; for (int j = i + 1; j<9; j++) if (s[j] < s[i]) tmp++; ans += tmp * f[9 - i - 1]; //f[]为阶乘 } return ans; //返回该字符串是全排列中第几大,从0开始 } int bfs() { memset(vis, false, sizeof(vis)); //初始化查找表 vis[cantor(st[1])] = true; int front = 1, rear = 2; while (front < rear) { State& s = st[front]; //用引用简化代码 int cs = cantor(s); if (memcmp(goal, s, sizeof(s)) == 0) return front; //找到目标状态,返回 int pos; for (pos = 0; pos < 9; pos++) if (!s[pos]) break; int x = pos / 3, y = pos % 3; for (int d = 0; d < 4; d++) { int newx = x + dirx[d]; int newy = y + diry[d]; int newpos = newx * 3 + newy; if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) { State& t = st[rear]; memcpy(&t, &s, sizeof(s)); t[newpos] = 0; t[pos] = s[newpos]; dis[rear] = dis[front] + 1; int ct = cantor(t); if (!vis[ct]) //结点合法且未访问,加入查找表,修改队尾指针 { rear++; curop[ct] = cop[d]; fa[ct] = cs; vis[ct] = true; } } } front++; //扩展完毕后修改队首指针 } return 0; } int main() { char c; for (int i = 0; i < 9; i++) { cin >> c; if (c == 'x') st[1][i] = 0; else st[1][i] = c - '0'; } goal[8] = 0; for (int i = 0; i < 8; i++) goal[i] = i + 1; int ans = bfs(); if (!ans) printf("unsolvable\n"); else { stack<char>ansop; int np = cantor(goal); int sp = cantor(st[1]); while (fa[np] != sp) { ansop.push(curop[np]); np = fa[np]; } ansop.push(curop[np]); while (!ansop.empty()) { printf("%c", ansop.top()); ansop.pop(); } printf("\n"); } }
//境界3:逆向广搜+康托展开+打表 //答案和样例的不同,但没关系,八数码问题是有多个解的,有Special Judge #include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<string> using namespace std; const int maxn = 363000; bool vis[maxn]; string path[maxn]; //方便的存下路径 const int dirx[] = { -1, 1, 0, 0 }; const int diry[] = { 0, 0, -1, 1 }; const char cop[] = { 'd', 'u', 'r', 'l' }; //和上面的方向数组相反,因为是逆向bfs const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; struct node { int map[9]; int pos; }que[maxn]; //用数组实现栈,stl代价太大 //直接求逆序数,因为移动x并不会改变map的逆序数的奇偶性,很容易想到 //而原始逆序数为0,故无解的情况是:逆序数%2!=0 int getnixu(int s[]) { int ret = 0; for (int i = 0; i < 9; i++) { if (s[i] == 0) continue; for (int j = 0; j < i; j++) { if (s[j] == 0) continue; if (s[i] < s[j]) ret++; } } return ret; } //康托展开 int cantor(int s[]) { int ans = 0; for (int i = 0; i<9; i++) { int tmp = 0; for (int j = i + 1; j<9; j++) if (s[j] < s[i]) tmp++; ans += tmp * f[9 - i - 1]; //f[]为阶乘 } return ans; //返回该字符串是全排列中第几大,从0开始 } void bfs() { memset(vis, false, sizeof(vis)); int tag[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 }; //设置最终状态 int ct_tag = cantor(tag); vis[ct_tag] = true; path[ct_tag] = ""; memcpy(que[1].map , tag, sizeof(tag)); que[1].pos = 8; int front = 1, rear = 2; while (front < rear) { node loc = que[front]; int ct_loc = cantor(loc.map); int x = loc.pos / 3, y = loc.pos % 3; for (int d = 0; d < 4; d++) { int newx = x + dirx[d]; int newy = y + diry[d]; int newpos = newx * 3 + newy; if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) { node noc; memcpy(noc.map, loc.map, sizeof(noc.map)); noc.map[newpos] = 0; noc.map[loc.pos] = loc.map[newpos]; noc.pos = newpos; int ct_noc = cantor(noc.map); if (!vis[ct_noc]) { que[rear++]=noc; vis[ct_noc] = true; path[ct_noc] = cop[d] + path[ct_loc]; } } } front++; } } int main() { bfs(); char c; while (cin >> c) { int g[15]; if (c == 'x') g[0] = 0; else g[0] = c - '0'; for (int i = 1; i < 9; i++) { cin >> c; if (c == 'x') g[i] = 0; else g[i] = c - '0'; } int temp = cantor(g); if (getnixu(g) % 2) cout << "unsolvable" << endl; else cout << path[temp] << endl; } }
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<string> using namespace std; const int maxn = 363000; int vis[maxn]; string path[maxn]; const int dirx[] = { -1, 1, 0, 0 }; const int diry[] = { 0, 0, -1, 1 }; const char zop[] = { 'u', 'd', 'l', 'r' }; const char cop[] = { 'd', 'u', 'r', 'l' }; const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; int tag[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 0 }; int start[9]; int spos; struct node { int map[9]; int pos; }que[maxn]; int getnixu(int s[]) { int ret = 0; for (int i = 0; i < 9; i++) { if (s[i] == 0) continue; for (int j = 0; j < i; j++) { if (s[j] == 0) continue; if (s[i] < s[j]) ret++; } } return ret; } int cantor(int s[]) { int ans = 0; for (int i = 0; i<9; i++) { int tmp = 0; for (int j = i + 1; j<9; j++) if (s[j] < s[i]) tmp++; ans += tmp * f[9 - i - 1]; } return ans; } string bfs_d() { memset(vis, 0, sizeof(vis)); memcpy(que[1].map, tag, sizeof(tag)); que[1].pos = 8; int ct_tag = cantor(tag); vis[ct_tag] = 2; path[ct_tag] = ""; memcpy(que[2].map, start, sizeof(start)); que[2].pos = spos; int ct_start = cantor(start); vis[ct_start] = 1; path[ct_start] = ""; int front = 1, rear = 3; while (front < rear) { node loc = que[front]; int ct_loc = cantor(loc.map); int x = loc.pos / 3, y = loc.pos % 3; for (int d = 0; d < 4; d++) { int newx = x + dirx[d]; int newy = y + diry[d]; int newpos = newx * 3 + newy; if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) { node noc; memcpy(noc.map, loc.map, sizeof(noc.map)); noc.map[newpos] = 0; noc.map[loc.pos] = loc.map[newpos]; noc.pos = newpos; int ct_noc = cantor(noc.map); if (!vis[ct_noc]) { que[rear++] = noc; vis[ct_noc] = vis[ct_loc]; if(vis[ct_noc]==2) path[ct_noc] = cop[d] + path[ct_loc]; else path[ct_noc] = path[ct_loc] + zop[d]; } else if (vis[ct_noc]!=vis[ct_loc]) { if (vis[ct_noc] == 1) return path[ct_noc] +cop[d]+ path[ct_loc]; else return path[ct_loc] +zop[d]+ path[ct_noc]; } } } front++; } return "unsolvable"; } int main() { char c; for (int i = 0; i < 9; i++) { cin >> c; if (c == 'x') { start[i] = 0; spos = i; } else start[i] = c - '0'; } if (getnixu(start) % 2) cout << "unsolvable" << endl; else cout << bfs_d() << endl; }
#include<iostream> #include<cstdio> #include<cstring> #include<stack> #include<string> using namespace std; const int maxn = 363000; bool vis[maxn]; int pre[9][maxn]; char preop[9][maxn]; int dis[9][maxn]; const int dirx[] = { 1, 0, 0, -1 }; const int diry[] = { 0, -1, 1, 0 }; const char cop[] = "dlru"; const int f[] = { 1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880 }; int tag[9][9]; void getTag() { for (int px = 0; px < 9; px++) { int j = 1; for (int i = 0; i < 9; i++) { if (i == px) tag[px][i] = 0; else tag[px][i] = j++; } } } struct node { int map[9]; int pos; }que[maxn]; //康托展开 int cantor(int s[]) { int ans = 0; for (int i = 0; i<9; i++) { int tmp = 0; for (int j = i + 1; j<9; j++) if (s[j] < s[i]) tmp++; ans += tmp * f[9 - i - 1]; } return ans; } void bfs(int px) { memset(vis, false, sizeof(vis)); int ct_tag = cantor(tag[px]); vis[ct_tag] = true; dis[px][ct_tag] = 0; pre[px][ct_tag] = -1; memcpy(que[1].map, tag[px], sizeof(tag[px])); que[1].pos = px; int front = 1, rear = 2; while (front < rear) { node loc = que[front]; int ct_loc = cantor(loc.map); int x = loc.pos / 3, y = loc.pos % 3; for (int d = 0; d < 4; d++) { int newx = x + dirx[d]; int newy = y + diry[d]; int newpos = newx * 3 + newy; if (newx >= 0 && newx < 3 && newy >= 0 && newy < 3) { node noc; memcpy(noc.map, loc.map, sizeof(noc.map)); noc.map[newpos] = 0; noc.map[loc.pos] = loc.map[newpos]; noc.pos = newpos; int ct_noc = cantor(noc.map); if (!vis[ct_noc]) { que[rear++] = noc; vis[ct_noc] = true; preop[px][ct_noc] = cop[d]; pre[px][ct_noc] = ct_loc; dis[px][ct_noc] = dis[px][ct_loc] + 1; } } } front++; } } void output(int p, int np) { if (pre[p][np] == -1) return; output(p, pre[p][np]); printf("%c", preop[p][np]); } int main() { getTag(); for (int i = 0; i < 9; i++) bfs(i); int casen; cin >> casen; for (int cas = 1; cas <= casen; cas++) { string s1, s2; cin >> s1 >> s2; int p; int g1[9], g2[9]; int ji = 1; for (int i = 0; i < 9; i++) { if (s1[i] == 'X') { p = i; g1[0] = 0; } else g1[s1[i]-'0'] = ji++; } for (int i = 0; i < 9; i++) { if (s2[i] == 'X') g2[i] = g1[0]; else g2[i] = g1[s2[i] - '0']; } int temp = cantor(g2); printf("Case %d: %d\n", cas, dis[p][temp]); output(p, temp); printf("\n"); } }