pku 1077 Eight

做了好几天,几个小错误,没耐心都没调试出来..

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;

}

你可能感兴趣的:(pku)