bfs-九宫格

题目 1426: [蓝桥杯][历届试题]九宫重排

题目描述:
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
在这里插入图片描述
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

样例输入:
12345678.
123.46758
样例输出:
3

解题思路:
1、对于求最短路径选择bfs;
2、然后要进行排重可以使用哈希函数map映射或者使用康拓判重;
康拓公式:X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a21!+a10!,
an代表的是当前这个数的后面比他小的有几位。
样例:数231在123的全排列下面是第X+1个数
2的后面有1个数比2小,即 1 * 2!
3的后面有1比3小的数,即 1* 1!
1的后面没有比1小的数,即 0 * 0!
X = 12!+11!+0*0! = 3
123的全排列: 123 132 213 231 312 321

代码一:使用map判重(耗时:948)
借鉴了:
https://blog.csdn.net/qq_41923622/article/details/86649189

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue> 
#include<map>
#include<cstring>
using namespace std;
char start[3][3];//记录开始的状态 
char gogal[3][3];// 记录目标状态

struct node {
	int x;//记录空白行的位置 
	int y;//记录空白列的位置 
	int step;//走的步数记录 
	char map[3][3];//记录状态 
}; 
queue<node>q;//这个其实是bfs的精华部分,就是利用他才可以算出最短路径 
map<string,int>v;
int dx[]={-1,0,1,0};//上左下右 
int dy[]={0,-1,0,1}; 

int judge(node cur){//判断是否达到要求 
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			if(cur.map[i][j]!=gogal[i][j]){
				return 0;
			}
		}
	}
	return 1;
} 

int check(node next){//这里就算是判重 
	string s="";
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++){
			s+=next.map[i][j];//把数字组成字符串 
		}
	}
	if(v[s]>0) return 0; //因为map定义string,int ,我们学过map的话知道int的值是靠string来寻找,如果string这个字符串的int 大于0的话那么之前就一定有 
	v[s]++;//这个就是累加字符串出现的次数 
	return 1; 
}

int bfs(int x1,int y1){
	node cur,next;//定义2个结构体变量
	 cur.x=x1;
	 cur.y=y1;
	 cur.step=0;
	 for(int i=0;i<3;i++){
	 	for(int j=0;j<3;j++){
	 		cur.map[i][j]=start[i][j];
		 }
	 }//上面是把当前状态放进结构体中
	if(judge(cur)){//判断第一次是不是就是目标,这个是特殊情况可能存在 
		return cur.step;
	} 
    q.push(cur);//把当前状态放进去,其实这也是bfs中的模板
	while(!q.empty()) {
		cur=q.front();//取出队列中的第一个
		q.pop();//取出之后就要删除,标准用语就是出队
		for(int i=0;i<4;i++){
			next.x=cur.x+dx[i];
			next.y=cur.y+dy[i];
			if(next.x>=0&&next.x<3&&next.y>=0&&next.y<3){//这里主要是判断下标是否越界,如果越界就相当于当前的方向是不可以走
			    next.step=cur.step+1;
				for(int j=0;j<3;j++)
				{
					for(int k=0;k<3;k++){
						next.map[j][k]=cur.map[j][k];
					}
				 } 
				 swap(next.map[next.x][next.y],next.map[cur.x ][cur.y]);//得到可以移动的位置,这里是实现移动 
				 if(check(next)){//移动了之后,现在就要进行1、把他写入map中进行判重,2.进行判断是个是终极目标 
				 	if(judge(next)){
				 		return next.step;
					 }
					 	q.push(next);
					 
				 }
			} 
		} 
	}
	return -1;//如果不能实现这个方案就是放回-1 

}



int main(){
 
 int x1,y1;//记录起始位置 
    for(int i=0;i<3;i++)
    {
    	for(int j=0;j<3;j++){
    		cin>>start[i][j];
    		if(start[i][j]=='.'){
    			x1=i;
    	        y1=j;		
			} 
		}
	}
    for(int i=0;i<3;i++){
    	for(int j=0;j<3;j++){
    		cin>>gogal[i][j];
		}
	}

	cout<<bfs(x1,y1)<<endl;

	return 0;
} 

代码二:使用康拓公式 (耗时:249)
借鉴:
https://blog.csdn.net/Crystal_viv/article/details/79386871

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<queue> 
#include<cstring>
using namespace std;
char start[3][3];//记录开始的状态 
char gogal[3][3];// 记录目标状态

struct node {
	int x;//记录空白行的位置 
	int y;//记录空白列的位置 
	int step;//走的步数记录 
	char map[3][3];//记录状态 
}; 
queue<node>q;//这个其实是bfs的精华部分,就是利用他才可以算出最短路径 
int kantuo[362880]; //9!=362880,进行判断是否出现重复

int a[9];
int fac[]={1,1,2,6,24,120,720,5040,40320};//这个是数字1-8的阶乘值 
int dx[]={-1,0,1,0};//上左下右 
int dy[]={0,-1,0,1}; 

int judge(node cur){//判断是否达到要求 
	for(int i=0;i<3;i++)
	{
		for(int j=0;j<3;j++)
		{
			if(cur.map[i][j]!=gogal[i][j]){
				return 0;
			}
		}
	}
	return 1;
} 

int check(node next){//这里就算是判重 
   int x=0;
   for(int i=0;i<3;i++){
   	for(int j=0;j<3;j++){
   		a[x++]=next.map[i][j]-'0';
	   }
   }
   int sum=0,s=0;//s代表第几个数 
   for(int i=0;i<9;i++){
   	for(int j=i+1;j<9;j++){
   		if(a[i]>a[j]){
   			sum++;
		   }
	   }
	   s+=sum*fac[8-i]; 
	   sum=0;
   }
   if(kantuo[s]>0) {
   	return 0;
   } 
   kantuo[s]++;
	return 1; 
}

int bfs(int x1,int y1){
	node cur,next;//定义2个结构体变量
	 cur.x=x1;
	 cur.y=y1;
	 cur.step=0;
	 for(int i=0;i<3;i++){
	 	for(int j=0;j<3;j++){
	 		cur.map[i][j]=start[i][j];
		 }
	 }//上面是把当前状态放进结构体中
	if(judge(cur)){//判断第一次是不是就是目标,这个是特殊情况可能存在 
		return cur.step;
	} 
    q.push(cur);//把当前状态放进去,其实这也是bfs中的模板
	while(!q.empty()) {
		cur=q.front();//取出队列中的第一个
		q.pop();//取出之后就要删除,标准用语就是出队
		for(int i=0;i<4;i++){
			next.x=cur.x+dx[i];
			next.y=cur.y+dy[i];
			if(next.x>=0&&next.x<3&&next.y>=0&&next.y<3){//这里主要是判断下标是否越界,如果越界就相当于当前的方向是不可以走
			    next.step=cur.step+1;
				for(int j=0;j<3;j++)
				{
					for(int k=0;k<3;k++){
						next.map[j][k]=cur.map[j][k];
					}
				 } 
				 swap(next.map[next.x][next.y],next.map[cur.x ][cur.y]);//得到可以移动的位置,这里是实现移动 
				 if(check(next)){//移动了之后,现在就要进行1、把他写入map中进行判重,2.进行判断是个是终极目标 
				 	if(judge(next)){
				 		return next.step;
					 }
					 	q.push(next);
					 
				 }
			} 
		} 
	}
	return -1;//如果不能实现这个方案就是放回-1 

}



int main(){
 
 int x1,y1;//记录起始位置 
    for(int i=0;i<3;i++)
    {
    	for(int j=0;j<3;j++){
    		cin>>start[i][j];
    		if(start[i][j]=='.'){
    			x1=i;
    	        y1=j;		
			} 
		}
	}
    for(int i=0;i<3;i++){
    	for(int j=0;j<3;j++){
    		cin>>gogal[i][j];
		}
	}

	cout<<bfs(x1,y1)<<endl;

	return 0;
} 

原题链接:https://www.dotcpp.com/oj/problem1426.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值