Post

AcWing845 - 八数码

题设

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

例如:

1
2
3
1 2 3
x 4 6
7 5 8

在游戏过程中,可以把 x 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1
2
3
1 2 3
4 5 6
7 8 x

例如,示例中图形就可以通过让 x 先后与右、下、右三个方向的数字交换成功得到正确排列。

交换过程如下:

1
2
3
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
1 2 3 
x 4 6 
7 5 8 

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

输出格式

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

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

输入样例:

1
2 3 4 1 5 x 7 6 8

输出样例

1
19

题解

可以利用bfs来求最短路,但难点在于每一步之后的状态表示比较复杂,且两种状态之间的距离表示较麻烦。

  • 状态表示:字符串”1234x5678”
  • 距离存储:unordered_map
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
#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;

int bfs(string start) {
  string end = "12345678x";
  
  queue<string> q;
  unordered_map<stirng, 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();
    
    int distance = d[t];
    
    if (t == end) return distance;
    
    int k = t.find('x');
    int x = k / 3, y = k % 3;
    
    for (int i = 0; i < 4; i++) {
      int nx = x + dx[i], ny = y + dy[i];
      if (nx >= 0 && nx <= 3 && ny >= 0 && ny < 3) {
        swap(t[k], t[nx * 3 + ny]); // 状态更新
        
        if (!d.count(t)) {
          d[t] = distance + 1;
          q.push(t);
        }
        swap(t[k], t[nx * 3 + ny]); // 状态恢复
      }
    }
  }
  
  return -1;
}

int main() {
  string start;
  for (int i = 0; i < 9; i++) {
    char c;
    cin >> c;
    start += c;
  }
  
  cout << bfs(start) << endl;
  
  return 0;
}
This post is licensed under CC BY 4.0 by the author.