题目描述:
如下面第一个图的九宫格中,放着 1~8 的数字卡片,还有一个格子空着。与空格子相邻的格子中的卡片可以移动到空格中。经过若干次移动,可以形成第二个图所示的局面。
我们把第一个图的局面记为:12345678.
把第二个图的局面记为:123.46758
显然是按从上到下,从左到右的顺序记录数字,空格记为句点。
本题目的任务是已知九宫的初态和终态,求最少经过多少步的移动可以到达。如果无论多少步都无法到达,则输出-1。
输入:
输入第一行包含九宫的初态,第二行包含九宫的终态。
输出:
输出最少的步数,如果不存在方案,则输出-1。
样例输入:
12345678.
123.46758
样例输出:
3
思考:
这题给出了初始矩阵和终点矩阵,我们可以从头和从尾同时开始搜索,边搜边标记,如果标记重合就表示找到了解。考察:1.字符串和数组之间的转化 2.BFS 3.队列
代码:
#include<bits/stdc++.h>
using namespace std;
int x,y;
int flag; //标记
string str1,str2,first,last;
map<string,int>dis,vis; //用map可以方便查询同时去重,dis表示操作次数,vis标记从哪来
queue<string>q1,q2; //头,尾对于两个队列
char m[3][3];
const int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; //四个方向
bool in(int x,int y){
return x>=0&&x<3&&y>=0&&y<3;
}//判断是否越界
void toM(string str){
for(int i=0;i<str.size();i++){
m[i/3][i%3]=str[i];
}
}//字符串转化为数组
string toS(){
string str;
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
str.push_back(m[i][j]);
}
}
return str;
}//数组转化为字符串
int doublebfs(){
q1.push(first);
dis[first]=1;
vis[first]=1;
q2.push(last);
dis[last]=1;
vis[last]=2;
while(!q1.empty()&&!q2.empty()){
if(q1.size()<q2.size()){
str1=q1.front();
q1.pop();
flag=1;
}else{
str1=q2.front();
q2.pop();
flag=2;
}
toM(str1);
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(m[i][j]=='.'){
x=i;
y=j;
}
}
}
for(int i=0;i<4;i++){
int tx=x+dir[i][0];
int ty=y+dir[i][1];
if(in(tx,ty)){
swap(m[x][y],m[tx][ty]);
str2 = toS();
if(!dis.count(str2)){ //判断是否已经被标记过
dis[str2]=dis[str1]+1;
vis[str2]=vis[str1];
if(flag==1){
q1.push(str2);
}else if(flag==2){
q2.push(str2);
}
}
else{
if(vis[str1]+vis[str2]==3){
int ans = dis[str1]+dis[str2]-1; //这里-1是因为设定一开始操作数为1
return ans;
}
}
swap(m[x][y],m[tx][ty]);//回溯!!!!!
}
}
}
return -1;//没找到
}
int main(){
cin>>first>>last;
if(first==last){
cout<<0<<endl;
}else{
cout<<doublebfs()<<endl;
}
return 0;
}