问题描述
如下面第一个图的九宫格中,放着 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