做了好几天,几个小错误,没耐心都没调试出来..
A* 最小堆 hash表 还是超时..诶
告一段落...
/* 分析: A*搜索的状态包含了布局和在搜索树中的深度. 因为在优先队列的优先级是f,所以无法实现布局的二分查找.但是在open和close表中又要查找布局.不可行.. 通过hash表来记录已经搜索过的布局的最小f,那么不需要在堆中查找,如果f较大,不入队列,也能保证最短步数. 按照从上到下,从左到右的顺序,八数码就是123456780,9个数字的排列是9!. */ #include <iostream> #include <cmath> #include <stack> using namespace std; const int MAX = 1 << 20; int step; const int EndPos[9][2] = {{2, 2}, {0, 0}, {0, 1}, {0, 2}, {1, 0}, {1, 1}, {1, 2}, {2, 0}, {2, 1}}; const int hashFac[9] = {1, 2, 6, 24, 120, 720, 5040, 40320}; int hash_table[MAX]; int heap_last;//堆中最后一个元素的下标(可插入位置-1) const int dir[4][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}};//r,u,l,d const int EndBoard[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 0}; struct Status { int board[3][3]; int r, c; int d;//移动方向 int g,h; Status *father; }start, End, Queue[MAX], Step[MAX];;//用堆来实现的优先队列,第一个元素的小标是1 int HFun(const Status &s) { int tile, cnt = 0; for(int r = 0; r < 3; r++) for(int c = 0; c < 3; c++) { tile = s.board[r][c]; if(0 != tile) cnt += abs(EndPos[tile][0] - r) + abs(EndPos[tile][1] - c); } return cnt; } bool operator<(const Status &s1, const Status &s2) { return ((s1.g + s1.h) < (s2.g + s2.h));//这里刚开始的时候把h也写成了g.. } int HashVal(const Status &s)//全排列hash函数,不会冲突 { int i,j,k = 0,array[9]; for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) array[k++] = s.board[i][j]; int cnt = 0, res = 0; for(i = 0; i < 9; i++) { cnt = 0; for(j = 0; j < i; j++) if(array[j] > array[i]) cnt++; res += cnt * hashFac[i]; } return res; } bool Empty() { return 0 == heap_last; } //向下维护小顶堆 void down_min_heap() { Status s = Queue[1]; int i,j; for(i = 1, j = i<<1; j <= heap_last; j = j << 1) { if(j + 1 <= heap_last && Queue[j + 1] < Queue[j])//较小孩子节点的下标 j++; if(Queue[j] < s) { Queue[i] = Queue[j];//元素上移 i = j; } else break; } Queue[i] = s; } Status Pop() { Status s = Queue[1]; Queue[1] = Queue[heap_last--]; down_min_heap(); return s; } /* 向上维护小顶堆 父亲结点必定小于另外一个孩子结点 所以如果该结点小于父亲结点,那么也小于另外一个孩子结点,直接往上移动就好 否则已经满足小顶堆特性 */ void up_min_heap() { int i,j; Status s = Queue[heap_last]; for(j = heap_last, i = j>>1; i >= 1; i = i >> 1) { if(s < Queue[i]) { Queue[j] = Queue[i]; j = i; } else break; } Queue[j] = s; } void Push(const Status &s) { Queue[++heap_last] = s; up_min_heap(); } void Trace(); bool Astar() { memset(hash_table, 0, sizeof(hash_table)); int r, c, nr, nc, tile, f, hashVal; start.g = 0; start.h = HFun(start); hash_table[HashVal(start)] = start.g + start.h; Push(start); Status s, ns; while(!Empty()) { s = Pop(); Step[step++] = s; if(!memcmp(s.board, EndBoard, sizeof(EndBoard))) { End.father = &s; Trace(); return true; } r = s.r; c = s.c; for(int d = 0; d < 4; d++) { if(abs(s.d - d) == 2) { continue; } nr = r + dir[d][0]; nc = c + dir[d][1]; if(nr < 0 || nr > 2 || nc < 0 || nc > 2) continue; ns = s; ns.d = d; tile = ns.board[nr][nc]; ns.board[r][c] = tile; ns.board[nr][nc] = 0; ns.r = nr; ns.c = nc;//这里刚开始写成了c,结果调试的时候发现经常2个0..无限蛋疼!!! ns.g++; ns.h = HFun(ns); f = ns.g + ns.h; hashVal = HashVal(ns); if(hash_table[hashVal] < f)//未访问或者估价更小的状态 { hash_table[hashVal] = f; ns.father = &Step[step - 1]; Push(ns); } } //cout<<heap_last<<endl; } return false; } void input() { int tile; for(int r = 0; r < 3; r++) for(int c = 0; c < 3; c++) { cin>>tile; if(cin.fail()) { cin.clear(); cin.ignore(1, ' '); start.r = r; start.c = c; start.board[r][c] = 0; } else { start.board[r][c] = tile; } /*if(!tile) { start.r = r; start.c = c; } */ } start.d = 100;//第一次不定义方向 } bool Solveable() { int i, j, array[9], k = 0, sum = 0; for(i = 0; i < 3; i++) for(j = 0; j < 3; j++) array[k++] = start.board[i][j]; for(i = 8; i >= 0; i--) for(j = i - 1; j >= 0; j--) if(array[j] && array[j] < array[i]) sum++; if(sum % 2 == 0) return true; else return false; } void Trace() { stack<int> trace; Status *p = End.father; while(p) { trace.push(p->d); p = p->father; } while(!trace.empty()) { int d = trace.top(); trace.pop(); switch(d) { case 0: cout<<'r'; break; case 1: cout<<'u'; break; case 2: cout<<'l'; break; case 3: cout<<'d'; break; } } cout<<endl; } int main() { //freopen("in.txt", "r", stdin); input(); if(Solveable()) Astar(); else cout<<"unsolvable"<<endl; return 0; }