题目描述
在一个3×3的网格中,1~8这8个数字和一个“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
算法1
BFS + unordered_map
知识点
状态表示复杂(1.如何把状态存到队列中2.如何记录每一个状态的距离)
1.用字符串表示状态
queue[HTML_REMOVED] q;
2.用哈希表存dist
unorder_map[HTML_REMOVED] d;
状态转移复杂(1.恢复成3*3的矩阵2.把x上下左右四个位置分别枚举并转移到x的位置上去3.再恢复成字符串)
C++ 代码
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>
using namespace std;
int bfs(string state)
{
queue<string> q;
unordered_map<string, int> d; //存储当前状态到下一状态的距离
q.push(state); //初始状态加入队列
d[state] = 0; //起始距离为0
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
string end = "12345678x";
while (q.size())
{
auto t = q.front();
q.pop();
int distance = d[t];
if (t == end) return d[t];
//状态转移
int k = t.find('x'); //找到x的下标
int x = k / 3, y = k % 3; //将字符串转成表格
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) //判断交换之后的t是否存在,而不是判断交换前
{
swap(t[a * 3 + b], t[k]); //将表格转成字符串
if (!d.count(t)) //如果当前状态未出现
{
d[t] = distance + 1;
q.push(t);
}
swap(t[a * 3 + b], t[k]); //恢复现场
//状态还原是为了对同一个位置的x 进行拓展操作
}
}
}
return -1;
}
int main()
{
char s[2];
string state;
for (int i = 0; i < 9; i ++ )
{
cin >> s;
state += *s;
}
cout << bfs(state) << endl;
return 0;
}
注意这里转载 滑稽_ωノ 大佬的代码
https://www.acwing.com/user/myspace/index/2776/
算法2
BFS + 全排列哈希(康托展开)
知识点
康托展开和逆康托展开
C++ 代码
#include<cstring>
#include<iostream>
#include<queue>
using namespace std;
int fact[9];
bool vis[362880];
int permutation_hash(char s[]) //求长度为9的字符串某种排列的哈希值
{
int ans = 0;
for(int i = 0; i < 9; i ++)
{
int d = 0;
for(int j = 0; j < i; j ++)
if(s[j] > s[i]) d ++; //求s[i]与其前面的字符组成的逆序对个数
ans += d * fact[i];
}
return ans;
}
typedef struct{
char s[10];
int step;
int k; //'x'在第k位
}Point;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = { 0,-1, 0, 1};
int bfs(Point p)
{
vis[permutation_hash(p.s)] = true;
queue<Point> q;
q.push(p);
while(!q.empty())
{
p = q.front();
q.pop();
/*
printf("%d ",p.step); //print调试法
puts(p.s);
*/
if(!strcmp(p.s , "12345678x")) return p.step;
int x = p.k / 3; //'x'的行数
int y = p.k % 3; //'x'的列数
Point next;
next.step = p.step + 1;
for(int i = 0; i < 4; i ++)
{
int nx = x + dx[i];
int ny = y + dy[i];
if(nx >= 0 && nx <= 2 && ny >= 0 && ny <= 2)
{
next.k = nx * 3 + ny; //求出'x'在字符串中的的新位置
strcpy(next.s, p.s);
next.s[9] = 0;
next.s[p.k] = p.s[next.k]; //先用即将和'x'交换的字符覆盖'x'之前的位置
next.s[next.k] = 'x'; //再给'x'的新位置赋值'x'
int hash = permutation_hash(next.s);
if(!vis[hash])
{
vis[hash] = true;
q.push(next);
}
}
}
}
return -1;
}
int main()
{
fact[0] = 1;
for(int i = 1; i < 9; i ++) fact[i] = fact[i - 1] * i; //预处理fact[i] = i!
char c[2],str[10];
Point start;
for(int i = 0; i < 9; i ++)
{
scanf("%s",&c);
if(c[0] == 'x') start.k = i;
start.s[i] = c[0];
}
start.s[9] = 0;
start.step = 0;
printf("%d",bfs(start));
return 0;
}
作者:滑稽_ωノ
链接:https://www.acwing.com/solution/content/2481/