poj 1077--Eight(八数码问题,BFS+康托展开+路径打印)

题意:经典的八数码问题,题目已确定好目标状态,若从起始状态到目标状态有解,输出最短的到达路径的每一步操作;否则输出unsolvable。

思路:这是一道经典的八数码问题,采用BFS+康托展开判重,主要多了一步打印路径,把可变化位置x看成数字0放置位置,共有9个数字,即9!种状态,在数组可开的范围内,用pre结构体记录当前状态的前驱状态即转化过来的方式,再用栈倒过来打印答案即可。(BFS时用STL中的queue会超时…故得手写队列,顺便手写了栈)

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=362880;//9!(把空位置x看成数字0)
char op[]={'u','d','l','r'};//上,下,左,右; 
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//方向数组要与op[]对应,便于操作; 
struct node
{
	int state[9];
	int step;
	int id;
};
struct pre//存前驱节点的标号和从前驱节点到当前节点的操作下标; 
{
	int id;
	int operation;
}pre[maxn+10];
bool vis[maxn+10]; 
int start[9],target[9];//起始状态,终止状态; 
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//阶乘表(0~9);
node que[maxn*2];
int sta[maxn+10];
int front,rear;//队头,队尾指针; 
int top;//栈顶指针; 
 
bool Cantor(int str[],int n)//康托展开判重; 
{
	int result=0;
	for(int i=0;i<=n;i++)
	{
		int count=0;
		for(int j=i+1;j<=n;j++)
		{
			if(str[i]>str[j])
			{
				++count;
			}
		}
		result+=(count*fac[n-i]);
	}
	if(!vis[result])
	{
		vis[result]=true;
		return true;//表示还没走过; 
	}
	else
	{
		return false;
	}
} 

void get_ans(node now)
{	
	int pos=now.id;
	while(pre[pos].id!=-1)
	{
		sta[top++]=pre[pos].operation;
		pos=pre[pos].id;
	}
	
	while(top!=0)
	{
		int now=sta[--top];
		printf("%c",op[now]);
	}
	printf("\n");
}
 
void bfs()
{
	node s;
	memcpy(s.state,start,sizeof(s.state));
	s.id=0;
	pre[s.id].id=-1;
	pre[s.id].operation=-1;
	s.step=0;
	Cantor(s.state,8);
	que[rear++]=s;
	int pos=0;//枚举当前是第几个状态,便于回溯打印路径; 
	
	while(front<rear)
	{
		node now=que[front++];
		if(memcmp(now.state,target,sizeof(target))==0)//成功; 
		{
			get_ans(now);
			return ;
		}
		int i;
		for(i=0;i<=8;i++)//找可转移状态的点0; 
		{
			if(now.state[i]==0)
			{
				break;
			}
		}
		//转化成3*3矩阵,左上角为(0,0); 
		int x=i/3;
		int y=i%3;
		for(int j=0;j<4;j++)
		{
			node in;
			int dx=x+dir[j][0];
			int dy=y+dir[j][1];
			if(dx<0||dx>=3||dy<0||dy>=3) continue;
			
			int where=dy+3*dx;//转化为一维;
			memcpy(in.state,now.state,sizeof(in.state));
			swap(in.state[where],in.state[i]);
			in.step=now.step+1;
			
			if(Cantor(in.state,8))//判重 
			{
				in.id=(++pos);
				pre[in.id].id=now.id;
				pre[in.id].operation=j;
				que[rear++]=in;	
			} 
		}
	}
	printf("unsolvable\n");
} 
 
int main()
{
	int i,j;
	for(i=0;i<=8;i++)
	{
		char c[2];
		scanf("%s",c);
		if(c[0]>='0'&&c[0]<='9')
		{
			start[i]=c[0]-'0';
		}
		else
		{
			start[i]=0;
		}
	}
	for(i=0;i<=8;i++)//构造终止状态; 
	{
		if(i!=8)
		{
			target[i]=i+1;
		}
		else
		{
			target[i]=0;
		}
	}
	memset(vis,false,sizeof(vis));
	front=rear=0;
	top=0;
	bfs();
	return 0;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值