#include <cstring> #include <cstdlib> #include <cstdio> #define INPUT using namespace std; /** Problem : poj1077,hdu1043,经典的八数码问题。 知识点: BFS + HASH + 打表 + 父亲节点记录 境界:3 - BFS+HASH+打表 A了两天!!! 记得拿启发式,双向BFS重新写一遍 IDA*就不做要求了。。。 思路: 1.打表 + 输出 将每个状态记录成0..9的一个排列。0代表的是'x'的位置。 每个状态用康托展开,求出自己的唯一id,然后搜索的下一个状态的 fa[id_next] = id_now,然后move[id_next] = i; dis[id_next] = dis[id_now] + 1; 这样,根据目标状态,求出其对应的康托展开,然后根据 fa[]数组就能找到每个移动。dis是指从1234567890到目标节点的最短steps 根据CLRS,BFS能找到两个节点中的最短路径。 注意边界,我们是反向搜索的,从目标状态往回搜索是这么搜索的: int j = id_dest; for(int k = dis[id_dest]; k > 0 ; k--,j = fa[j] ) { printf("%d",move[j]); } 因为达到了dest 就不再移动,所以move[id_dest]记录的是最后一次的移动 一点一点往前输出也就是了,但是注意记录的是从"1234567890"移动到目标 状态的方式,所以对于 1234567890 的向左移动,在这里就变成了向右移动 (也就是说,把整个过程反过来),所以要反着输出。 2.搜索 搜索的时候判重问题,重复的状态不再进行搜索,利用hash表进行搜索, 按照PP的解释这样查询的效率是O(1) 每个状态看做一个整数,然后 hash_id = nownum % HASHKEY; 注意hash表下标一定要从1开始!!! 也就是下文的now从1开始!否则next[hash_id]可以为0!!那就恶心死了! while(next[hash_id]) { if states[ next[hash_id] ] == nownum return 0; //插入失败 hash_id = next[ hash_id ]; } next[ hash_id ] = now; states[now] = nownum; now++; return true; 如果hashkey相同且发现了相同的状态,那么就返回失败,如果返回失败,那么就不展开该节点进行搜索了。 教训: 这题正向不行就应该想到打表,尤其是康托展开+逆序数的应用是一个亮点!详细看 Cantor那个代码。cantor[]这个数组还记录了排列所需要用的阶乘数目。 这道题双向BFS,A*,IDA*都能做,所以是一道好题,第三重境界,还需要加油! 还有输出的反向输出,父节点的记录,各个都是那么巧妙,这道题要多看几遍 */ const int c0de4fun = 364880; const int HASHKEY = 1000003; //////////////////l r u d //////////////////r,l,d,u const int dx[] = {0,0,-1,1}; const int dy[] = {-1,1,0,0}; const int cantor[] = {1,1,2,6,24,120,720,5040,40320}; //int vals[c0de4fun]; int head[HASHKEY]; int next[HASHKEY]; int states[HASHKEY]; int queue[c0de4fun]; int dis[c0de4fun]; int fa[c0de4fun]; int move[c0de4fun]; int now = 1,front = 1 ,rear = 2; int _dest[9]; int hashk(int node) { return node % HASHKEY; } bool insert(int node) { int key = hashk(node); while ( next[key] ) { if( states[ next[key] ] == node ) return false; key = next[key]; } next[key] = now; states[now] = node; now++; return 1; } void segNum(int* tmp,int node) { for(int i = 8 ; i >= 0 ; i--) { tmp[i] = node % 10; node /= 10; } } int getNum(int* tmp) { int res = 0; for(int i = 0 ; i < 9 ; i++) { res = res*10 + tmp[i]; } return res; } int getCanto(int *a) { int ans = 0 ; int i,j,cnt; for( i = 0 ; i < 9 ;i++) { cnt = 0; for( j = i + 1; j < 9; j++) { // if(a[i] < a[j]) cnt++; if( a[i] > a[j] ) cnt++; } ans = ans + cantor[ 8 - i ] * cnt; } return ans; } void bfs(int node) { int i,x,y,newx,newy,z,newz; int tmp[9]; int tmp1[9]; int tnode,tnode1; // int front = 1 ,rear = 2; fa[0] = 0; dis[0] = 0; queue[front] = node; insert(node); while(front < rear) { tnode = queue[front]; //insert(tnode); segNum(tmp,tnode); for( i = 0 ; i < 9; i++) { if ( tmp[i] == 0 ) break; //找到X的位置 } x = i / 3 ; y = i % 3; z = i; for( i = 0 ; i < 4 ; i++) { newx = x + dx[i]; newy = y + dy[i]; if( newx >= 0 && newx < 3 && newy >=0 && newy < 3) { ///合法移动 newz = newx * 3 + newy; memcpy(tmp1,tmp,sizeof(tmp)); tmp1[newz] = tmp[z]; tmp1[z] = tmp[newz]; tnode = getNum(tmp1); if ( insert(tnode) ) { int cant_tmp = getCanto(tmp); int cant_tmp1 = getCanto(tmp1); ///这里用康托展开给tnode一个值,然后 ///也给tnode1一个值,fa[kan_tnode1] = kan_node; ///然后move[kan_node] = 对应值。 fa[cant_tmp1] = cant_tmp; move[cant_tmp1] = i; dis[cant_tmp1] = dis[cant_tmp] + 1; queue[rear] = tnode; rear++; } } } front++; } #ifdef TEST for(int i = 0 ; i < 9 ; i++) { printf("%d ",tmp[i]); } #endif } void Solve() { int k = getNum(_dest); int l = 0; int ind; bool isSoluted = false; for(int i = 1 ; i < rear; i++) { if ( k == queue[i] ) { // printf("Can be solved\n"); isSoluted = true; break; } } if( isSoluted ) { ind = getCanto(_dest); l = ind; for( k = dis[ind] ;k > 0;k--,l = fa[l]) { switch(move[l]) { case 0 : // printf("l "); printf("r"); break; case 1: // printf("r"); printf("l"); break; case 2: // printf("u "); printf("d"); break; case 3: printf("u"); // printf("d "); break; } // printf("%d ",move[k]); } printf("\n"); // printf("Solved\n");*/ } else { printf("unsolvable\n"); } } int main() { char tmp1; int i = 0; #ifdef INPUT freopen("b:\\acm\\poj1077\\input.txt","r",stdin); #endif bfs(123456780); while ( scanf("%c",&tmp1) != EOF ) { if (tmp1 == ' ' || tmp1 == '\n') continue; if (tmp1 == 'x' ) { _dest[i] = 0; } else { _dest[i] = tmp1 - '0'; } i++; if( i % 9 == 0) { Solve(); i = 0; } } return 0; }