做了好几天,几个小错误,没耐心都没调试出来..
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;
}