AcWing 845. 八数码
宋标 Lv5

题目

在一个 的网格中, 个数字和一个 x 恰好不重不漏地分布在这 的网格中。

例如:

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

现在,给你一个初始网格,请你求出得到正确排列至少需要进行多少次交换。

输入格式

输入占一行,将 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3 
x 4 6 
7 5 8

则输入为:1 2 3 x 4 6 7 5 8

输出格式

输出占一行,包含一个整数,表示最少交换次数。

如果不存在解决方案,则输出

输入样例:

2  3  4  1  5  x  7  6  8

输出样例

19

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <iostream>
#include <queue>
#include <unordered_map>
#include <algorithm>

using namespace std;

// 这题比较秒的解法是存储string,可以很方便为bfs创建不同互不影响的副本
int bfs(string start)
{
string end = "12345678x";
if (start == end) return 0;

queue<string> q;
unordered_map<string, int> map; // 状态和变化次数
int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};

q.push(start);
while (q.size())
{
auto t = q.front(); q.pop();

int x, y;
for (int i = 0; i < t.size(); ++ i)
if (t[i] == 'x')
{
x = i / 3, y = i % 3;
break;
}

for (int i = 0; i < 4; ++ i)
{
string state = t;
int a = x + dx[i], b = y + dy[i];
if (a >= 0 && a < 3 && b >= 0 && b < 3)
{
swap(state[x * 3 + y], state[a * 3 + b]);
if (!map.count(state))
{
map[state] = map[t] + 1;
if (state == end) return map[state];
q.push(state);
}
}
}
}

return -1;
}

int main()
{
string s;
char c;
for (int i = 0; i < 9; ++ i)
cin >> c, s += c;
cout << bfs(s) << endl;
return 0;
}
 评论