一、问题描述
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
二、输入格式
测试点1
12345678.
123.46758
测试点2
13524678.
46758123.
三、输出格式
测试点1答案
3
测试点2答案
22
四、题解
一般求搜索题目求最短路径都是用bfs来实现的。但是这道题我想着用双向广度优先搜索来实现。
双向广度搜索,就是比广度优先搜索再多一个搜索,这个搜索是从终点向起点搜索。那么这样子我们就有两个搜索了,一个从起点向终点搜索,一个从终点在起点搜索,那么当两个搜索都找到同一个子状态时,搜索就结束了。这样子就可以提高搜索的效率了。
现在我用画图来模拟一下搜索过程(部分过程)。
那么当数据规模很大时,我们采用双向搜索就可以提高效率,降低时间复杂度了。
这道题还有一个可优化的点,就是将二位数组压成一位数组,如下所示。
那么如果将二位数组转换成一维数组,在交换相邻那么数值,要怎么表示呢?
如果是在二维数组的时候,交换两个数组,我们可以用两个数组,分别是dx,dy。
dx[]={0,0,1,-1}; dy[]={1,-1,0,0};通过这两个数组能够实现当前元素同上下左右四个方向进行交换
那么,当压缩成一位数组的时候,我们同样可以用一个方向数组dir。这个dir数组应该这样设计
dir={1,-1,3,-3},其中1,-1,代表原本二维数组向左向右。3,-3代表原本二维数组向上向下。
然后我们还需要创建两个map,分别来存储正向搜索和反向搜索找到的子状态,当这两个map有相同子状态时,那么我们程序就结束了,直接将正向搜索的步数和反向搜索的步数相加。
五、代码实现
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <queue>
#include <map>
using namespace std;
map <string, int> M1;
map <string, int> M2;
typedef pair <string, int> P;
int dir[4] = {1, -1, 3, -3};
string s, e;
queue <P> Q1;
queue <P> Q2;
bool judge(int x){
if(x < 0 || x > 8)
return false;
else
return true;
}
int bfs(){
P p;
p.first = s;
p.second = 0;
Q1.push(p);
p.first = e;
p.second = 0;
Q2.push(p);
while(!Q1.empty() && !Q2.empty()){
if(!Q1.empty()){
p = Q1.front();
Q1.pop();
string str = p.first;
int step = p.second;
int index = str.find('.');
for(int i = 0; i < 4; ++ i){
int curIndex = index + dir[i];
string newStr = str;
char ch = newStr[index];
newStr[index] = newStr[curIndex];
newStr[curIndex] = ch;
int newStep = step + 1;
if((i == 0 && (index == 2 || index == 5))||(i == 1 && (index == 3 || index == 6)))
continue;
if(judge(curIndex)){
if(M1[newStr])
continue;
M1[newStr] = newStep;
if(M2[newStr])
return M1[newStr] + M2[newStr];
P next;
next.first = newStr;
next.second = newStep;
Q1.push(next);
}
}
}
if(!Q2.empty()){
p = Q2.front();
Q2.pop();
string str = p.first;
int step = p.second;
int index = str.find('.');
for(int i = 0; i < 4; ++ i){
int curIndex = index + dir[i];
string newStr = str;
char ch = newStr[index];
newStr[index] = newStr[curIndex];
newStr[curIndex] = ch;
int newStep = step + 1;
if((i == 0 && (index == 2 || index == 5))||(i == 1 && (index == 3 || index == 6)))
continue;
if(judge(curIndex)){
if(M2[newStr])
continue;
M2[newStr] = newStep;
if(M1[newStr])
return M1[newStr] + M2[newStr];
P next;
next.first = newStr;
next.second = newStep;
Q2.push(next);
}
}
}
}
return -1;
}
int main()
{
int ans;
cin >> s >> e;
M1[s] = 0;
M2[s] = 0;
ans = bfs();
if(ans == -1)
cout << "-1" << endl;
else
cout << ans << endl;
return 0;
}