• 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;
    }
    
  • 相关阅读:
    php 计算两个日期相差天数
    Linux系统查找清理磁盘大文件
    虚拟机重启网络服务失败,当查看状态显示错误Failed to start LSB......
    c++简单编写线性表(实验)
    学校的c++程序课程设计(简单的写法 并无太多c++的特色)
    C语言简单实现链栈基本几个功能
    简单用数组模拟顺序栈(c++)
    详细易懂的二叉树遍历(先中后)
    大数加法之C语言函数法(只有正数版)
    有关Java垃圾回收的几个问题
  • 原文地址:https://www.cnblogs.com/steady/p/1941733.html
Copyright © 2020-2023  润新知