【蓝桥杯】九宫重排(BFS+康拓展开)

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

解题思路:采用类似于迷宫老鼠求最短路问题,利用BFS找所有的情况,关键问题在于判重,我们知道一共有9!种可能,利用康拓展开,就可以确定每种排列位于所有排列中的位置。

康拓展开
定义:X=an*(n-1)!+an-1*(n-2)!+…+ai*(i-1)!+…+a21!+a10!
2的后面有1个数比2小,即 1 * 2!
1的后面没有比1小的数,即 0 * 1!
3的后面没有比3小的数,即 0 * 0!
X = 12!+01!+0*0! = 2

这样我们声明一个数组st[9!]来记录哪个状态被访问过了;同时我们声明一个二维数组rec[9!][9]来表示某个状态的排列是什么。

代码如下

#include <iostream>
#include <queue>
using namespace std;

int jing[4][4] = {0};

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

struct poi{ //记录0在二维表中的位置,以及当前排列的状态(即所有排列中的位置),以及到这个位置的步数
	int x; //横坐标
	int y;//纵坐标
	int state;//排列状态
	int step = 0;//到该的步数
	poi(int xx,int yy,int st,int ste){
		x = xx;
		y = yy;
		state = st;
		step = ste;
	}
};

queue<poi>que; //维护一个队列

int rec[362880][9];//记录所有状态对应的排列
int st[362880] = {0}; //记录那个状态被访问过了

int kangtuo(int x[])  //康拓展开求位置
{
	int fac[]={1,1,2,6,24,120,720,5040,40320};
	int i,j,t,sum;
	sum = 0;
	for(i=0;i<9;i++)
	{
		t = 0;
		for(j=i+1;j<9;j++)
		{
			if(x[j]<x[i])
				t++;
		}
		sum = sum+t*fac[8-i];
	}
	return sum;
}

poi des(1,1,1,0); //目标排列,参数随便写的,后面会赋值

int bfs(int tx,int ty,int sta){ //起始位置tx,ty以及起始状态
	que.push(poi(tx,ty,sta,0)); //压入第一个位置
	while(!que.empty()){
	//	cout<<que.size()<<endl;
		poi tp = que.front(); //取出一个位置
		int x = tp.x;
		int y = tp.y;
		que.pop();
		int*tarr = rec[tp.state];
		for(int i = 1;i <= 3;i++){ // 把当前排列状态所对应的一维排列转换成二维的
			for(int j = 1;j <= 3;j++){
				jing[i][j] = tarr[3*(i - 1) + j  - 1]; 
			}
		}
		if(tp.state == des.state){ //当前状态即为目标状态,找到了
			return tp.step;
		}
		for(int i = 0;i < 4;i++){ //像四个方向移动
			 x = x + dir[i][0];
			 y = y + dir[i][1];
			 if(x >= 1 && x <= 3 && y >= 1 && y <= 3){ //越界判断
				//移动#############
				int t  = jing[x][y];
				jing[x][y] = jing[x - dir[i][0]][y - dir[i][1]];
				jing[x - dir[i][0]][y - dir[i][1]] = t;
				///移动########################
				int tarr[9];
				for(int i = 1; i <= 3;i++){
					for(int j = 1;j <= 3;j++){
						tarr[3*(i - 1) + j - 1] = jing[i][j];
					}
				}
				int stt = kangtuo(tarr);
				//装换成状态 
				if(!st[stt]){ //此状态没有被访问过
					que.push(poi(x,y,stt,tp.step + 1));//压入新的状态,步数为上一状态步数+1
					st[stt] = 1;
					for(int i = 0;i < 9;i++){
						rec[stt][i] = tarr[i];
					}
				}
				//恢复移动################
				t  = jing[x][y];
				jing[x][y] = jing[x - dir[i][0]][y - dir[i][1]];
				jing[x - dir[i][0]][y - dir[i][1]] = t;
				//恢复移动###############
			 }
			 x = x - dir[i][0];
			 y = y - dir[i][1];
		} 
	}
	return -1;
}

int main(){
	string s1;
	string s2;
	cin>>s1;
	cin>>s2;
	int sa[9] = {0};
	int sb[9] = {0};
	int start = 0;
	for(int i = 0;i < 9;i++){
		if(s1[i] == '.'){ //‘.’不处理,视为0
			start = i; 
		}else{
			sa[i] = s1[i] - '0';
		}
		if(s2[i] != '.'){
			sb[i] = s2[i] - '0';
		}
	}
	int st1 = kangtuo(sa); //起始排列状态
	int st2 = kangtuo(sb); //目标排列状态
	des.state = st2;
	for(int i = 0;i < 9;i++){ //把起始排列按状态存入rec中
		rec[st1][i] = sa[i];
	}
	st[st1] = 1; //认为起始状态被访问过了
	cout<<bfs(start / 3 + 1,start % 3 + 1,st1);
	return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值