现在的位置: 首页 > 综合 > 正文

杭电 1043 Eight (A*搜索)

2012年02月19日 ⁄ 综合 ⁄ 共 2777字 ⁄ 字号 评论关闭

http://acm.hdu.edu.cn/showproblem.php?pid=1043

看了一晚的A*,早上的时候终于可以看懂这些个代码了。

很早之前收藏过一个讲A*的帖子,可惜一直没怎么看明白,目测链接也找不到了,BFS果断的TEL了,然后,,,就没有然后了,看discuss不是双BFS就是A*,算了,还是学一下吧,毕竟挺喜欢搞游戏的,虽然目前为止没弄出过什么像样的东西来。运气还不错,搜到了几个非常好的博客,讲的很详细。

其中的康托展开被用来做哈希,一开始还不怎么明白啥玩意叫康托,百科了一下,基本了解个大概。

A*的确在迷宫搜索方面厉害,下面这几个博客是学习参考的,推荐一下【突然想到了,谷歌百度的地图搜索是不是也用的A* 呢?】:
http://www.cnblogs.com/ambition/archive/2011/07/25/search_plus.html

http://baike.baidu.com/view/437641.htm

http://acm.uestc.edu.cn/bbs/read.php?tid=2810

http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<cctype>
using namespace std;

struct S
{
	char maze[3][3];
	int x,y;
	int g,h,f;
	S(){}
	S(const S & ts)
	{
		memcpy(maze,ts.maze,sizeof(maze));
		x=ts.x; y=ts.y;
		g=ts.g; h=ts.h; f=ts.h;
	}
	friend bool operator <(const S&a, const S&b )
	{
		if(a.f==b.f)
			return a.g<a.g;
		return a.f>b.f;
	}
}s;

const int fac[]={1,1,2,6,24,120,720,5040,40320};
bool vis[363000];
int pre[363000];
char op[363000];

//唐拓展开求哈希
int inv_hash(S ts)
{
	char str[10];
	int ans=0;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
		{
			str[i*3+j]=ts.maze[i][j];
			int cnt=0;
			for(int k=i*3+j-1;k>=0;k--)
				if(str[k]>str[i*3+j]) cnt++;
			ans+=fac[i*3+j]*cnt;
		}
	return ans;
}
//求h值,h值求的其实就是当前状态中的8个数的位置与目的位置的偏移值
const int pos[][2]={{0,0},{0,1},{0,2},{1,0},{1,1},{1,2},{2,0},{2,1},{2,2}};
int Abs(int x)  {return x<0?-x:x;}

int h(S ts)
{
	int val=0;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
		{
			if(ts.maze[i][j]=='x')
				continue;
			int c=ts.maze[i][j]-'1';
			val+=Abs(pos[c][0]-i)+Abs(pos[c][1]-j);
		}
		return val;
}

const int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};

bool bfs()
{
	memset(vis,0,sizeof(vis));

	priority_queue<S>qu;
	qu.push(s);
	while(!qu.empty())
	{
		S u=qu.top();   qu.pop();
		int ihu=inv_hash(u);

		for(int i=0;i<4;i++)
		{
			S v=u;
			v.x+=dir[i][0];
			v.y+=dir[i][1];
			if(v.x<0 || v.x>=3 || v.y<0 || v.y>=3 ) continue;
			{//这两行代码相当于移动X
				v.maze[u.x][u.y]=u.maze[v.x][v.y];
				v.maze[v.x][v.y]='x';
			}
			//重新计算g,h,f值
			v.g+=1;  v.h=h(v); v.f=v.g+v.h;
			int ihv=inv_hash(v);
			if(vis[ihv]) continue;
			vis[ihv]=1;
			//记录父节点
			pre[ihv]=ihu;
			if(i==0) op[ihv]='d';
			else if(i==1) op[ihv]='r';
			else if(i==2) op[ihv]='u';
			else if(i==3) op[ihv]='l';
			if(ihv==0)
				return true;
			qu.push(v);
		}
	}
	return false;
}

inline bool inv_check()
{
	char str[10];
	int cnt=0;
	for(int i=0;i<3;i++)
		for(int j=0;j<3;j++)
		{
			str[i*3+j]=s.maze[i][j];
			if(str[i*3+j]=='x')continue;
			for(int k=i*3+j-1;k>=0;k--)
			{
				if(str[k]=='x') continue;
				if(str[k]>str[i*3+j])
					cnt++;
			}
		}
	return !(cnt&1);
}

char in[100];
char stk[100];

int main()
{
	while(gets(in))
	{
		for(int i=0,x=0,y=0;in[i];i++)
		{
			if((isdigit(in[i])) || in[i]=='x')
			{
				s.maze[x][y]=in[i];
				if(in[i]=='x') {s.x=x;  s.y=y;}
				y++;
				if(y==3)
					y=0,x++;
			}
		}
		if(!inv_check())
		{
			puts("unsolvable");
			continue;
		}
		s.g=0;  s.h=h(s);  s.f=s.h;

		//如果输入数据就是目标状态
		int shash=inv_hash(s);
		if(shash==0)
		{
			puts("");continue;
		}

		bfs();
		//输出结果
		int top=-1,thash=0;
		while(thash!=shash)//开始很不明白为什么输出的时候是从0开始,哈希值怎么可能会是0呢
		{//琢磨了一会才反应过来,当到达最终状态的时候由于不存在逆序对,所以哈希值一定是0,然后倒着退出来。
			stk[++top]=op[thash];
			thash=pre[thash];
		}
		for(int i=top;i>=0;i--)
			putchar(stk[i]);
		puts("");
	}
}

抱歉!评论已关闭.