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;
}
原文地址:https://www.cnblogs.com/steady/p/1941733.html