一、理解与感悟
-
为什么将二维转一维?
输入的是一个一维数据,就像是一个字符串。二维数组如果想对比每一次走完的棋盘是否一致,需要双重循环,比较麻烦,所以想出了一个用一维模拟二维的方法。 -
二维怎么转一维?
一维下标=tx * 3 + ty
以行号、列号下标从(0)开始为例,就是行号( imes 3 +) 列号,比如:
行号为(1)(第二行),列号为(1)(第二列),那么(1 imes 3+1=4),就是在一维中是第(5)个(下标从(0)开始)。
- 一维如何还原成二维?
int x = k / 3, y = k % 3;
二、完整代码
#include <bits/stdc++.h>
using namespace std;
int bfs(string s) {
//终点的样子
string end = "12345678x";
//广度搜索套路
queue<string> q;
//将当前棋盘入队列
q.push(s);
//记录距离
unordered_map<string, int> d;
d[s] = 0; //s节点的距离是0,就是无需变动
//dx数组
int dx[] = {-1, 0, 1, 0};
int dy[] = {0, 1, 0, -1};
while (!q.empty()) {
auto t = q.front();
q.pop();
//距离
int dist = d[t];
//判断是不是到目标值了,这里就能看出字符串模拟的好处了,要不就是两个二维数组的双重循环对比,麻烦。
if (t == end) return dist;
//状态转移
int k = t.find('x');
//一维数组转化为二维数组的坐标,是一个常用技巧
//除3就是行,模3就是列,x,y下标从0开始
int x = k / 3, y = k % 3;
//四个方向
for (int i = 0; i < 4; i++) {
//走一步后的坐标
int tx = x + dx[i], ty = y + dy[i];
//不出界
if (tx >= 0 && tx < 3 && ty >= 0 && ty < 3) {
//调整位置
//二维数组转一维数组的技巧
swap(t[k], t[tx * 3 + ty]);
if (!d.count(t)) { //这个位置以前没有人走过
d[t] = dist + 1; //距离需要+1
q.push(t); //放入队列
}
swap(t[tx * 3 + ty], t[k]);
}
}
}
return -1;
}
int main() {
//读入优化
ios::sync_with_stdio(false);
//开始的棋盘(字符之间用空格隔开,所以不能用string,需要cin+char解决)
string s;
char c;
for (int i = 1; i <= 9; i++) {
cin >> c;
s += c;
}
//广度优先搜索
cout << bfs(s) << endl;
return 0;
}