问题描述:
在一个 3×3 的网格中,从1∼8 这 88 个数字和一个 x
恰好不重不漏地分布在这 3×3 的网格中。
例如:
1 2 3
x 4 6
7 5 8
在游戏过程中,可以把 x
与其上、下、左、右四个方向之一的数字交换(如果存在)。
我们的目的是通过交换,使得网格变为如下排列(称为正确排列):
1 2 3
4 5 6
7 8 x
例如,示例中图形就可以通过让 x
先后与右、下、右三个方向的数字交换成功得到正确排列。
交换过程如下:
1 2 3 1 2 3 1 2 3 1 2 3
x 4 6 4 x 6 4 5 6 4 5 6
7 5 8 7 5 8 7 x 8 7 8 x
现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。
输入格式
输入占一行,将 3×3 的初始网格描绘出来。
例如,如果初始网格如下所示:
1 2 3
x 4 6
7 5 8
则输入为:1 2 3 x 4 6 7 5 8
输出格式
输出占一行,包含一个整数,表示最少交换次数。
如果不存在解决方案,则输出 −1。
输入样例
2 3 4 1 5 x 7 6 8
输出样例
19
问题解决:
对于bfs问题,大部分题目都是需要通过队列来解决的。解决该类问题的大致模板如下:
void dfs()
{
队列Q = {初始顶点};
while(Q.size()){
auto t = Q.front();
Q.pop();
访问 t;
Q.push(所有与t邻接且未被访问过的点);
标记u已经被访问过;
}
}
对于八数码问题,代码如下:
#include <bits/stdc++.h>
using namespace std;
int bfs(string start)
{
string end = "12345678x";
queue<string> q;
unordered_map<string,int> d;
q.push(start);
d[start] = 0;
int dx[4] = {-1,0,1,0},dy[4] = {0,1,0,-1};
while(q.size())
{
//取出队头元素
auto t = q.front();
q.pop();
if(t == end)
return d[t];
//找到该string下的x的位置,二维坐标
int position = t.find('x');
int x = position / 3 , y = position % 3;
int distance = d[t];
for(int i=0;i<4;i++)
{
//四个方向的扩展
int a = x + dx[i] , b = y + dy[i];
if(a >= 0 && a < 3 && b>=0 && b<3)//在3*3的范围内
{
swap(t[position],t[a*3+b]);
if(!d.count(t)) //如果这种情况以前没有发生过,就push
{
d[t] = distance + 1;
q.push(t);
}
swap(t[position],t[a*3+b]);
}
}
}
//若不存在就返回-1
return -1;
}
int main()
{
string s;
for(int i=0;i<9;i++)
{
char c;
cin>>c;
s += c;
}
cout<<bfs(s)<<endl;
return 0;
}