蓝桥杯 九宫重排(BFS)

问题描述   如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。

《蓝桥杯 九宫重排(BFS)》
《蓝桥杯 九宫重排(BFS)》

  我们把第一个图的局面记为:12345678.

  把第二个图的局面记为:123.46758

  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。

  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。 输入格式   输入第一行包含九宫的初态,第二行包含九宫的终态。 输出格式   输出最少的步数,如果不存在方案,则输出-1。 样例输入 12345678.

123.46758 样例输出 3 样例输入 13524678.

46758123. 样例输出 22 ————————————————————————————————————————————————————————————

涉及知识点:广度搜索BFS,哈希表

题目已知初态和终态,在初态到终态的过程中,会经过数个不同的状态,一个状态可以通过将空格朝上、下、左、右四个方向移动转移到另一个状态,这是不是有点像搜索找路?题目要求的是“最少移动多少步”,也就是最短距离,求最短距离的办法一般就很容易联想到迪杰斯特拉、广度搜索。这题在像是一个搜索题的前提下,我们就应该选用广度搜索来做。

这题我们把每一个状态看成一个节点,由一个状态到另一个状态是在搜索树上向下了一层。我用queen[MAX]作为广度搜索中用到的队列,用0表示输入的.。队列里的每个元素存储0~8九个数,用queen[i][9]记录当前节点是在搜索树上的第几层,初态是第0层,每多一层表示步数加一。

每个状态可以用一个唯一的数来表示,当状态的表示数与终态相同时,表示已经找到最短路径。表示的方式是,例如样例输入中的终态是123.46758,就记为int型数123046758。这题还需要记录到某个状态是否已经出现过,如果已经出现过就不要再加入队列进行BFS.这里我们用到哈希表,当一个状态要加入到队列前,先判断该状下的表示数是否在已经标记为搜索过,在哈希表hash[]中标记为true的元素表示为已经搜索过。用上述的方法表示的数的范围是12345678~9876543210,如果哈希表的空间开辟到9876543210这么大的话会超过限制。由于已知一个状态必然是9位数(包括0),用八位数就可唯一的表示出每个状态。小于12345678的数不会出现,所以把表示每个状态的数减去12345678,形成最终在程序里用到的表示状态的数。


代码如下:

#include<iostream>
#include <string.h>
using namespace std;
#define MAX 370000 
int queen[MAX][10],final[9];
int Zero[MAX];
int q_queen;
int final_hash_num;
bool hash[86419760];
int step;
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
int getHashNum(int H[])//获取该状态在哈希表中的位置 
{
	int num=0,i;
	for(i=0;i<8;i++)
	{
		num = num*10 + H[i];
	}
	return num-1234567;
}
bool SetHash(int num)
{
	if(hash[num])//该排列已经搜索过 
	{
		return false;
	}else{
		hash[num] = true;
		return true;
	}
} 
bool BFS()
{
	int zx,zy,nx,ny,z;
	int i=0,j,hashnum;
	while(i<q_queen)
	{
		hashnum = getHashNum(queen[i]);
		if(hashnum == final_hash_num)
		{
			step = queen[i][9];
			return true;
		}
		zx = Zero[i]/3;  //一维的表示变化为二位的矩阵表示 
        zy = Zero[i]%3;
        for(j=0;j<4;j++)
        {
        	nx = zx + dx[j]; //向上、下、左、右某个方向移动 
        	ny = zy + dy[j];
        	if(nx>2 || nx<0 || ny>2 || ny<0)
        	{
        		continue;
			}
			z = nx*3+ny;//移动后空格在一维情况下的第几个 
			memcpy(queen[q_queen+1], queen[i], sizeof(int)*10);
			queen[q_queen+1][Zero[i]] = queen[i][z];
			queen[q_queen+1][z] = 0;
			if(SetHash( getHashNum(queen[q_queen+1]) ))//如果之间没有到达过这个状态 
			{
				q_queen++;
			    queen[q_queen][9] = queen[i][9]+1;//记录步数 
			    Zero[q_queen] = z;				
			}
			
        }
        i++; 
	}	
	return false; 
}
int main()
{
	int i,j;
	char c;
	for(i=0;i<9;i++)
	{
		cin >> c;
		if(c == '.')
		{
			queen[q_queen][i] = 0;
			Zero[0] = i;
		}else{
			queen[q_queen][i] = c - '0';
		}
	}
	q_queen ++;
	queen[q_queen][9] = 0;//表示当前搜索到第0步
	for(i=0;i<9;i++)
	{
		cin >> c;
		if(c == '.')
		{
			final[i] = 0;
		}else{
			final[i] = c - '0';
		}
	}
	final_hash_num = getHashNum(final);
	//将初始状态和末状态的排列加入哈希表,表示该情况已经搜索到	
	SetHash(getHashNum(queen[0]));

	//广度搜索 
	if(BFS())
	{
		cout << step;
	}else{
		cout << -1;
	}
	return 0;
}

    原文作者:九宫格问题
    原文地址: https://blog.csdn.net/more_ugly_less_bug/article/details/55261240
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞