蓝桥杯 历届试题 九宫重排(bfs)

问题描述

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

  我们把第一个图的局面记为:12345678.
  把第二个图的局面记为:123.46758
  显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
  本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。

输入格式

  输入第一行包含九宫的初态,第二行包含九宫的终态。

输出格式

  输出最少的步数,如果不存在方案,则输出-1。

样例输入

12345678.
123.46758

样例输出

3

样例输入

13524678.
46758123.

样例输出

22

 

【解题思路】

这道题用了bfs+康拓判重

bfs既广度优先搜索,搜到的第一个就是最少步数。如何判断重复是一个问题,有很多人用哈希表哈希函数,本人学艺不精不会使用哈希(捂脸),康拓展开是数学上的一个公式,这个公式运用于判断一个数在该数全排列下第几个。

定义:X=an*(n-1)!+an-1*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!

eg:数213在123的全排列下面是第X+1个数

        2的后面有1个数比2小,即 1 * 2!

        1的后面没有比1小的数,即 0 * 1!

        3的后面没有比3小的数,即 0 * 0!

        X = 1*2!+0*1!+0*0! = 2

        123的全排列: 123  132  213 231 312 321

九宫最多有9!种排列组合,既362880中排列组合方式,使用数组state[362880]可以用来判断是否出现重复的元素。

使用bfs搜索需要使用队列这一数据结构,曾尝试过使用STL中的queue但是失败了(捂脸),下面代码使用得是队列先进先出的特性。

#include<iostream>
#include<string>
#include<cstring>
using namespace std;

int state[362880];  //判断是否重复
int step[362880];  //存储步数
int bnum,bkt;
int dx[] = {-1,1,0,0};  //上下左右
int dy[] = {0,0,-1,1};
typedef int type[9];
type q[362880];
type a,b;  //a为初始状态,b为最终状态
type tem,tem2;

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];
	}
	if(state[sum]==1)
		return 0;
	else
	{
		state[sum] = 1;
		return 1;
	}
}

int bfs()
{
	//bfs广度优先搜索,搜索到的第一个就是最短路径
	memcpy(q[0],a,sizeof(a));  //初始状态入队
	int front = 0;
	step[front] = 0;
	int rear = 1;
	int i,j,p,x,y,xx,yy;
	//type tem,tem2;
	while(front < rear)  //如果队列不为空
	{
		memcpy(tem,q[front],sizeof(tem));
		if(memcmp(tem,b,sizeof(b)) == 0)  //如果队首元素与最终元素相等
			return front;
		for(i=0;i<9;i++)  //找到0的位置
		{
			if(tem[i] == 0)
				break;
		}
		x = i/3;  //转换成二维坐标
		y = i%3;
		for(j=0;j<4;j++)  //进行空格上下左右移动
		{
			xx = x+dx[j];
			yy = y+dy[j];
			if(xx>=0 && yy>=0 && xx<3 && yy<3)  //如果移动没有越界
			{
				p = xx*3+yy;
				memcpy(tem2,tem,sizeof(tem));  //将tem拷贝到tem2上面
				tem2[i] = tem2[p];  //交换p和i的值,既移动0的位置
				tem2[p] = 0;
				if(kangtuo(tem2))
				{
					memcpy(q[rear],tem2,sizeof(tem2));
					step[rear] = step[front]+1;
					rear++;
				}
			}
		}
		front++;
	}
	return -1;
}

int main()
{
	memset(state,0,sizeof(state));
	int i,anum;
	string str1,str2;
	cin>>str1;
	cin>>str2;
	for(i=0;i<9;i++)  //将'.'转换成0
	{
		if(str1[i]>'0'&&str1[i]<='9')
			a[i] = str1[i]-'0';
		else
		{
			a[i] = 0;
			anum = i;
		}
	}
	for(i=0;i<9;i++)
	{
		if(str2[i]>'0'&&str2[i]<='9')
			b[i] = str2[i]-'0';
		else
		{
			b[i] = 0;
			bnum = i;
		}
	}
	int road = bfs();  //根据输出最后结果
	if(road == -1)
		cout<<road<<endl;
	else
		cout<<step[road]<<endl;
	return 0;
}

最后提一句,重排九宫就是经典bfs算法中的八数码,这里有收藏的八数码的八种境界,适合新手看一下

http://blog.csdn.net/kopyh/article/details/48442415#t7

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是C++实现广度优先算法实现九宫重排的示例代码: ```cpp #include <iostream> #include <queue> #include <vector> #include <map> using namespace std; // 目标状态 const vector<int> targetState = {1, 2, 3, 4, 5, 6, 7, 8, 0}; // 表示状态的结构体 struct State { vector<int> nums; // 状态数组 int zeroIndex; // 0的位置 int step; // 步数 State() {} State(vector<int> nums, int zeroIndex, int step) : nums(nums), zeroIndex(zeroIndex), step(step) {} }; // 广度优先搜索 int bfs(vector<int> startState) { queue<State> q; // 队列 map<vector<int>, bool> visited; // 记录已访问过的状态 State initState(startState, 0, 0); // 初始状态 q.push(initState); visited[startState] = true; while (!q.empty()) { State curState = q.front(); q.pop(); if (curState.nums == targetState) { // 已找到目标状态 return curState.step; } // 上下左右四个方向 int dx[4] = {-1, 1, 0, 0}; int dy[4] = {0, 0, -1, 1}; for (int i = 0; i < 4; i++) { int x = curState.zeroIndex / 3 + dx[i]; int y = curState.zeroIndex % 3 + dy[i]; if (x >= 0 && x < 3 && y >= 0 && y < 3) { // 位置合法 vector<int> newNums = curState.nums; swap(newNums[curState.zeroIndex], newNums[x * 3 + y]); if (!visited[newNums]) { // 新状态未曾访问过 State newState(newNums, x * 3 + y, curState.step + 1); q.push(newState); visited[newNums] = true; } } } } return -1; // 无法到达目标状态 } int main() { vector<int> startState = {2, 8, 3, 1, 6, 4, 7, 0, 5}; int step = bfs(startState); cout << step << endl; // 输出步数 return 0; } ``` 其中,`State`结构体表示一个状态,包括`nums`表示状态数组,`zeroIndex`表示0的位置,`step`表示从初始状态到达该状态的步数。`bfs`函数实现广度优先搜索,`startState`为初始状态数组,返回从初始状态到目标状态需要的步数。在搜索过程中,使用队列`q`存储待搜索的状态,使用`visited`记录已访问过的状态,遇到新状态时将其加入队列中并标记为已访问。每次从队列中取出一个状态,遍历其上下左右四个方向,生成新状态,并判断该状态是否已访问过,若未访问过则将其加入队列中。最终,若搜索完所有状态仍未找到目标状态,则返回-1表示无法到达目标状态。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值