hdu 1043 Eight BFS 康托展开

Eight

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 28349    Accepted Submission(s): 7540
Special Judge


Problem Description
The 15-puzzle has been around for over 100 years; even if you don't know it by that name, you've seen it. It is constructed with 15 sliding tiles, each with a number from 1 to 15 on it, and all packed into a 4 by 4 frame with one tile missing. Let's call the missing tile 'x'; the object of the puzzle is to arrange the tiles so that they are ordered as: 
 1  2  3  4
 5  6  7  8
 9 10 11 12
13 14 15  x

where the only legal operation is to exchange 'x' with one of the tiles with which it shares an edge. As an example, the following sequence of moves solves a slightly scrambled puzzle: 
 1  2  3  4     1  2  3  4     1  2  3  4     1  2  3  4
 5  6  7  8     5  6  7  8     5  6  7  8     5  6  7  8
 9  x 10 12     9 10  x 12     9 10 11 12     9 10 11 12
13 14 11 15    13 14 11 15    13 14  x 15    13 14 15  x
            r->            d->            r->

The letters in the previous row indicate which neighbor of the 'x' tile is swapped with the 'x' tile at each step; legal values are 'r','l','u' and 'd', for right, left, up, and down, respectively. 

Not all puzzles can be solved; in 1870, a man named Sam Loyd was famous for distributing an unsolvable version of the puzzle, and 
frustrating many people. In fact, all you have to do to make a regular puzzle into an unsolvable one is to swap two tiles (not counting the missing 'x' tile, of course). 

In this problem, you will write a program for solving the less well-known 8-puzzle, composed of tiles on a three by three 
arrangement.
 

Input
You will receive, several descriptions of configuration of the 8 puzzle. One description is just a list of the tiles in their initial positions, with the rows listed from top to bottom, and the tiles listed from left to right within a row, where the tiles are represented by numbers 1 to 8, plus 'x'. For example, this puzzle 

1 2 3 
x 4 6 
7 5 8 

is described by this list: 

1 2 3 x 4 6 7 5 8
 

Output
You will print to standard output either the word ``unsolvable'', if the puzzle has no solution, or a string consisting entirely of the letters 'r', 'l', 'u' and 'd' that describes a series of moves that produce a solution. The string should include no spaces and start at the beginning of the line. Do not print a blank line between cases.
 

Sample Input
 
 
2 3 4 1 5 x 7 6 8
 

Sample Output
 
 
ullddrurdllurdruldr
 

非常牛的题,我断断续续做了两天,中间头都快大了,最后学了不少东西,比如A* 和康托展开在BFS搜索上的应用。以后有时间还要继续做。建议做此题前请提前留出来起码半天的时间。
第一种:双向BFS
#include<cstdio>
#include<queue>
#include<string>
#include<string.h>
using namespace std;
//一个迷宫,假设其是没有边界,没有墙的空地,单BFS需要4^n时间,那双向BFS就是4^(n/2)*2,缩短了特别多。 
const int maxn=5e5+10;
int hs[9]={1,1,2,6,24,120,720,5040,40320};
struct node{
	int num;
	char ch;
}pre[maxn];

struct node2{
	string c;
	int num;
}e;

int vis[maxn];
int vis2[maxn];


int dir[4]={-3,3,-1,1};
char a[30],d[5]="udlr",d2[5]="durl";
string b="123456780";

void show(int x)//输出 
{
	if(pre[x].num==-1)
	return;
	show(pre[x].num);
	printf("%c",pre[x].ch);
}

int get_hash(string e)//获得该状态的康拓展开哈希值 
{
	int s=0,i,j,k;
	for(i=0;i<9;i++)
	{
		k=0;
		for(j=0;j<i;j++)
		if(e[j]>e[i])
		k++;
		s+=k*hs[i];
	}
	return s;
}

void bfs() //同时从起点状态和终点状态开始遍历,使其在中间相遇 
{
	vis[get_hash(e.c)]=1;
	pre[1].num=-1;
	int num=2,x,i,j,k,p,q;
	node2 f,g;
	f.c=b;
	f.num=8;
	pre[2].num=-1;
	vis2[get_hash(f.c)]=2;
	queue<node2>q1;
	queue<node2>q2;
	q1.push(e);
	q2.push(f);
	
	while(!q1.empty()&&!q2.empty())
	{
		f=q1.front();
		q1.pop();
		p=get_hash(f.c);
		if(vis2[p])
		{
			show(vis[p]);
			k=vis2[p];
			while(pre[k].num!=-1)
			{
				printf("%c",pre[k].ch);
				k=pre[k].num;
			}
			printf("\n");
			return;
		}
		for(i=0;i<4;i++)
		{
			if(i==0&&f.num<3) continue;
			if(i==1&&f.num>5) continue;
			if(i==2&&f.num%3==0)continue;
			if(i==3&&f.num%3==2)continue;
			x=f.num+dir[i];
			g=f;
			swap(g.c[f.num],g.c[x]);
			q=get_hash(g.c);
			if(vis[q])
			continue;
			num++;
			vis[q]=num;
			g.num=x;
			pre[num].num=vis[p];
			pre[num].ch=d[i];
			q1.push(g);
		}
		
		f=q2.front();  
        q2.pop();  
        p=get_hash(f.c);  
        //cout<<f.c<<endl;  
        if(vis[p])  
        {  
            show(vis[p]);  
            k=vis2[p];  
            while(pre[k].num!=-1)  
            {  
                printf("%c",pre[k].ch);  
                k=pre[k].num;  
            }  
            printf("\n");  
            return ;  
        }  
        for(i=0;i<4;i++)  
        {  
            if(i==0&&f.num<3)continue;  
            if(i==1&&f.num>5)continue;  
            if(i==2&&f.num%3==0)continue;  
            if(i==3&&f.num%3==2)continue;  
            x=f.num+dir[i];  
            g=f;  
            swap(g.c[f.num],g.c[x]);  
            q=get_hash(g.c);  
            if(vis2[q])continue;  
            vis2[q]=++num;  
            g.num=x;  
            pre[num].num=vis2[p];  
            pre[num].ch=d2[i];  
            q2.push(g);  
        }  
    }  
    printf("unsolvable\n");  	
}

int main()
{
	while(gets(a))
	{
		int i,j,k=0,n;
		n=strlen(a);
		e.c="";
		for(i=0,j=0;i<n;i++)
		{
			if(a[i]!=' ')
			{
				if(a[i]=='x')
				{
					e.num=j;
					e.c+='0';
				}
				else
				e.c+=a[i];
				j++;
			}
		}
		for(i=0;i<9;i++)
		{
			if(e.c[i]=='0')
			continue;
			for(j=0;j<i;j++)
			{
				if(e.c[j]=='0')
				continue;
				if(e.c[j]>e.c[i])
				k++;
			}
		}
		memset(vis2,0,sizeof(vis2));
		memset(vis,0,sizeof(vis));
		if(k&1)
		printf("unsolvable\n");
		else
		bfs();	
	}
	return 0;
}
 
 
第二种:A*
#include<stdio.h>
#include<queue>
#include<math.h>
#include<string.h>
using namespace std;


const int maxn=4e5+10;
int ha[9]={1,1,2,6,24,120,720,5040,40320};
int dir[4][2]={-1,0,1,0,0,-1,0,1};
char d[5]="udlr";
int vis[maxn];
int i,j,k;

struct node{
	int f[3][3];
	int x,y;
	int g,h;
	int hash_num;
	bool operator < (const node a)const{
		//return h==a.h? g>a.g : h>a.h;
		return h+g>a.h+a.g;
	}
};

struct path{
	int pre;
	char ch;
}p[maxn];

int  get_hash(node e) //康托展开,压缩空间 
{
	int a[9],ans=0;  
	k=0;
	for(i=0;i<3;i++)
	{
		for(j=0;j<3;j++)
			a[k++]=e.f[i][j];
	}
	for(i=0;i<9;i++)
	{
		k=0;
		for(j=0;j<i;j++)
			if(a[j]>a[i])
			k++;
		ans+=ha[i]*k;
	}
	return ans;
}

//评估函数,获得评估值 
//计算1~8的数字回到原点需要的步数作为评估值,一定是小于实际操作数的 

int get_h(node e)
{
	int ans=0;
	for(i=0;i<3;i++)
	{
		for(j=0;j<3;j++)
		{
			if(e.f[i][j])
			ans+=abs(i-(e.f[i][j]-1)/3)+abs(j-(e.f[i][j]-1)%3);
		}
	}
	return ans;
}

void print(int x)
{
	if(p[x].pre==-1)
	return;
	print(p[x].pre);
	printf("%c",p[x].ch);
}

void A_star_BFS(node e)
{
	int end_ans,xx,yy;  
	node a;
	for(i=0;i<9;i++)
		a.f[i/3][i%3]=(i+1)%9;
	end_ans=get_hash(a);//记录目标状态的康拓展开哈希值 
	
	e.hash_num=get_hash(e);
	e.g=0;e.h=get_h(e);
	vis[e.hash_num]=1;
	p[e.hash_num].pre=-1;
	
	
	if(e.hash_num==end_ans)
	{
		printf("\n");
		return;
	}
	
	priority_queue<node> q;
	q.push(e);
	while(!q.empty())
	{
		e=q.top();
		q.pop();
		
		for(i=0;i<4;i++)
		{
			xx=e.x+dir[i][0];
			yy=e.y+dir[i][1];
			if(xx<0||yy<0||xx>=3||yy>=3)
			continue;
			a=e;
			swap(a.f[e.x][e.y], a.f[xx][yy]);
			k=get_hash(a);
			if(vis[k])
			continue;
			vis[k]=1;
			a.hash_num=k;
			a.x=xx;
			a.y=yy;
			a.g++;
			a.h=get_h(a);
			p[k].pre=e.hash_num;
			p[k].ch=d[i];
			if(k==end_ans)
			{
				print(k);
				printf("\n");
				return;
			}
			q.push(a);
		}
	}
}

int main()
{
	char a[30];
	while(gets(a))
	{
		int n;
		node e;
		n=strlen(a);
		for(i=0,j=0;i<n;i++) //数据处理 
		{
			if(a[i]==' ')continue;
			if(a[i]=='x')
			{
				e.f[j/3][j%3]=0;
				e.x=j/3;
				e.y=j%3;
			}
			else
			e.f[j/3][j%3]=a[i]-'0';
			j++;
		}
		//求出此状态逆序数
		for(i=0,k=0;i<9;i++)
		{
			if(e.f[i/3][i%3]==0)
			continue;
			for(j=0;j<i;j++)
			{
				if(e.f[j/3][j%3]==0)
				continue;
				if(e.f[j/3][j%3]>e.f[i/3][i%3])
				k++;
			}	
		} 
		memset(vis,0,sizeof(vis));
		if(k&1)
		printf("unsolvable\n");
		else
		A_star_BFS(e);
		
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值