本蒟蒻的第二篇题解
题目传送门
首先,看到这样状态变化并且要求出最小步数的问题,最便捷的方法就是搜索。考虑到时间复杂度,我们首选bfs。
在bfs中,比较重要的一个环节是判定状态是否已经被访问过。这里的棋盘如果用二维数组来存储,不论是访问还是标记都不是很方便。而题目的输入格式给了我们一些提示,因此,我们使用一个字符串来存储棋盘的状态。对于是否被访问的判定,我们可以选用一个map来解决,这是目前我认为最简单的方法。
这里每一次状态的变化都在字符串中完成,就不像普通的二维数组,可以直接模拟上下左右四个位置的变换,但是,我们可以从题目中所给的图中发现,0与它上下左右四个数的位置关系,在字符串下标中的体现是{-3,3,-1,1},最后在判定每次操作是否合法的时候,方法如下:
int dir[4] = {-3, 3, 1, -1};
bool in(int x) {
return x >= 0 && x < 9;
}
if ((in(tx) && i <= 1) || (in(tx) && i > 1 && x / 3 == tx / 3)) {
}
x为操作前‘0’的下标,tx为一次操作后‘0’的下标,i是dir数组中的下标,进行‘0’上下移动的时候,只需判定操作后‘0’的下标是否在0~8之间,进行‘0’左右移动的时候,还需要判定操作前后是否在棋盘的同一行。
最后附上AC代码:
#include <bits/stdc++.h>
using namespace std;
string st, sp;
map<string, int> h;
int dir[4] = {-3, 3, 1, -1};
bool flag;
bool in(int x) {
return x >= 0 && x < 9;
}
struct node{
string s;
int zb, cnt;
};
void bfs(int x, int c) {
queue<node> q;
q.push({st, x, c});
h[st] = 1;
while (!q.empty()) {
node now = q.front();
q.pop();
x = now.zb;
for (int i = 0; i < 4; i++) {
int tx = x + dir[i];
string s3 = now.s;
if ((in(tx) && i <= 1) || (in(tx) && i > 1 && x / 3 == tx / 3)) {
swap(s3[x], s3[tx]);
if (!h[s3]) {
if (s3 == sp) {
cout << now.cnt + 1 << endl;
flag = true;
return;
}
h[s3] = 1;
q.push({s3, tx, now.cnt + 1});
}
}
}
}
}
int main(){
cin >> st >> sp;
if (st == sp) {
cout << 0 << endl;
return 0;
}
int sx;
for (int i = 0; i < st.size(); i++) {
if (st[i] == '0') sx = i;
}
bfs(sx, 0);
if (!flag) cout << "impossible" << endl;
return 0;
}
如果有大佬有更好的算法,可以在评论中提出。