Problem Address:http://poj.org/problem?id=1077
Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=1043
【前言】
第一次写八数码问题。
这份代码写得也是够残的。
还好效率不是很低。
HDU的数据明显比POJ的强。而且HDU里有初末状态相同的情况,而POJ没有。
这也是后来改成双向A*之后在HDU上狂WA的原因。
提交了20多次之后才发现真相= =
【思路】
典型的八数码问题。
九宫格中有1-8的数字,空出一位用于数字的移动。求从一个状态转移到另一个状态的路径。
具体的存储方式可以到网上搜一下。
以下简单介绍几种应用于八数码问题的搜索算法。
DFS:
由于不知道步数,所以用DFS是不可取的。
BFS:
从初始状态开始,每一步向四个方向扩展。直到达到末状态。
简单的BFS使用的空间很大,时间也很大,当步数增加时节点数膨胀很快。
因为,就八数码问题,一般的BFS不太可取。
双向BFS:
从初状态和末状态同时开始扩展。扩展方式与一般BFS相同。
直到两者有交点时停止查找。
相比于一般的BFS,双向BFS无论是在空间还是时间方面,都有很大的提升。
A*启发式搜索:
利用启发式函数 f'() = a*g'() + b*h'(),g'()表示当前以使用代价(可用步数表示),h'()表示达到目标还需花费的代价(可用manhattan距离或者相同位置不同数字的个数表示)。
启发式搜索相对于前面的搜索提升是巨大的。访问的结点很少,空间和时间的使用也因此很少。
可以这样看,BFS都是A*的一种特殊形式。
当b=0且a!=0时,f'() = a*g'(),即只于结点所耗步数有关,即为一般的BFS。
所以,通过改变a和b的值可以在一定程度上提升搜索效率。
双向A*:
A*与双向BFS的结合。
【代码】
这份代码是双向A*,但是通过改变里面的参数可以实现BFS、双向BFS、A*等。
#include <iostream> #include <queue> using namespace std; const int maxhash = 400000; int start, end = 123456789; int le[9] = {1,10,100,1000,10000,100000,1000000,10000000,100000000}; int te[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320}; int manhattan[9][9] = { {0,1,2,1,2,3,2,3,4}, {1,0,1,2,1,2,3,2,3}, {2,1,0,3,2,1,4,3,2}, {1,2,3,0,1,2,1,2,3}, {2,1,2,1,0,1,2,1,2}, {3,2,1,2,1,0,3,2,1}, {2,3,4,1,2,3,0,1,2}, {3,2,3,2,1,2,1,0,1}, {4,3,2,3,2,1,2,1,0}}; int re[9]; struct HASH { bool visited; int value; int pre; char op; bool inqueue; int g; int h; bool opposite; }hash[maxhash]; struct cmp { bool operator()(const int &a, const int &b) { int fa = hash[a].g + 10*hash[a].h; int fb = hash[b].g + 10*hash[b].h; return fa>fb; } }; char get_opposite(char op) { switch(op) { case 'u': return 'd'; case 'd': return 'u'; case 'l': return 'r'; case 'r': return 'l'; default: return 'x'; } } priority_queue<int, vector<int>, cmp> q; void init_hash() { int i; for (i=0; i<maxhash; i++) { hash[i].g = hash[i].h = 0; hash[i].inqueue = false; hash[i].visited = false; hash[i].pre = -1; } } void init_rank(int x) { int i; int t = x%10; x /= 10; for (i=7; i>=0; i--) { if (i+1<t) re[x%10-1] = i; else re[x%10-1] = i+1; x /= 10; } re[8] = t-1; } bool check_even(int x) { int temp[8]; int i, j; x /= 10; for (i=7; i>=0; i--) { temp[i] = x%10; x /= 10; } int sum = 0; for (i=1; i<8; i++) { for (j=0; j<i; j++) { if (temp[i]<temp[j]) sum++; } } if (sum&1==1) return false; else return true; } int get_manhattan(int x, bool opposite) { int i; int h = 0; int t = x%10; x /= 10; if (opposite==false) { for (i=7; i>=0; i--) { if (i+1<t) h += manhattan[x%10-1][i]; else h += manhattan[x%10-1][i+1]; x /= 10; } } else { for (i=7; i>=0; i--) { if (i+1<t) h += manhattan[i][re[x%10-1]]; else h += manhattan[i+1][re[x%10-1]]; x /= 10; } } return h; } int move(char op, int x) { int t0, t1, t2; switch(op) { case 'u': t0 = 9 - x%10 + 1; t1 = x/le[t0]; t2 = t1%1000; t1 = t1-t2 + (t2%100)*10 + t2/100; t1 *= le[t0]; return (t1+(x%le[t0])-3); case 'r': return x+1; case 'd': t0 = 9 - x%10 - 2; t1 = x/le[t0]; t2 = t1%1000; t1 = t1-t2 + (t2%10)*100 + t2/10; t1 *= le[t0]; return (t1+(x%le[t0])+3); case 'l': return x-1; default: return x; } } int get_hash(int x) { int i, j, k; int sum = (9-x%10)*te[8]; x /= 10; int temp[8]; for (i=7; i>=0; i--) { temp[i] = x%10; x /= 10; } for (i=1; i<8; i++) { k = 0; for (j=0; j<i; j++) { if (temp[i]<temp[j]) k++; } sum += k*te[i]; } return sum; } void show(int x) { if (hash[x].pre==-1) return; show(hash[x].pre); printf("%c", hash[x].op); } void show_opposite(int x) { if (hash[x].pre!=-1) { printf("%c", get_opposite(hash[x].op)); show_opposite(hash[x].pre); } } bool valid(int x, char op, int pre) { int hx = get_hash(x); if (pre==-1 && !hash[hx].visited) { if (x==end) hash[hx].opposite = true; else hash[hx].opposite = false; hash[hx].value = x; hash[hx].h = get_manhattan(x, hash[hx].opposite); hash[hx].inqueue = true; hash[hx].visited = true; hash[hx].op = op; hash[hx].g = 0; hash[hx].pre = -1; q.push(hx); return true; } if (!hash[hx].visited) { hash[hx].g = hash[pre].g + 1; hash[hx].op = op; hash[hx].pre = pre; hash[hx].visited = true; hash[hx].inqueue = true; hash[hx].opposite = hash[pre].opposite; hash[hx].h = get_manhattan(x, hash[hx].opposite); hash[hx].value = x; q.push(hx); } else { if (hash[hx].opposite ^ hash[pre].opposite) { if (hash[hx].opposite) { show(pre); printf("%c", op); show_opposite(hx); } else { show(hx); printf("%c", get_opposite(op)); show_opposite(pre); } printf("\n"); return true; } else if (hash[hx].inqueue && hash[hx].g>hash[pre].g+1) { hash[hx].g = hash[pre].g + 1; hash[hx].op = op; hash[hx].pre = pre; if (!hash[hx].inqueue) { hash[hx].inqueue = true; q.push(hx); } } } return false; } bool expand(int t) { int j; int temp = t%10; int ht = get_hash(t); hash[ht].inqueue = false; if (temp>3) { j = move('u', t); if (valid(j, 'u', ht)) return true; } if (temp%3!=0) { j = move('r', t); if (valid(j, 'r', ht)) return true; } if (temp<7) { j = move('d', t); if (valid(j, 'd', ht)) return true; } if (temp%3!=1) { j = move('l', t); if (valid(j, 'l', ht)) return true; } return false; } int main() { char str[100]; int i, j; int t; while(cin.getline(str, 100)) { start = 0; for (i=0,j=0; str[i]!='\0'; i++) { if (str[i]!=' ') { if (str[i]=='x') { t = j + 1; } else { start = start*10 + (str[i]-'0'); j++; } } } start = start*10 + t; if (!check_even(start)) { printf("unsolvable\n"); continue; } if (start==end) { printf("\n"); continue; } init_hash(); init_rank(start); while(!q.empty()) q.pop(); valid(start, 'x', -1); valid(end, 'x', -1); while(!q.empty()) { int ht = q.top(); t = hash[ht].value; q.pop(); if (expand(t)) break; } } return 0; }