八数码问题题解
解题思路
算法选择
使用广度优先搜索(BFS)算法,因为:
- BFS按层搜索,保证找到的解决方案是最短路径(最少交换次数)
- 八数码问题的状态空间相对较小(9! = 362880种可能状态)
- BFS能够系统地探索所有可能的状态转移
关键数据结构
struct qj { vector<vector<char>> arr; // 当前3×3矩阵状态 int dep; // 已走步数(深度) pair<int, int> place; // 空白格位置 };
状态表示
使用vector<vector<char>>
表示3×3矩阵状态,用集合set<vector<vector<char>>>
记录已访问状态,避免重复处理。
代码实现详解
1. 初始化与输入处理
vector<vector<char>> math(3, vector<char>(3)); vector<vector<char>> goal(3, vector<char>(3)); set<vector<vector<char>>> vis; // 定义目标状态 goal[0][0] = '1', goal[0][1] = '2', goal[0][2] = '3'; goal[1][0] = '4', goal[1][1] = '5', goal[1][2] = '6'; goal[2][0] = '7', goal[2][1] = '8', goal[2][2] = 'x'; // 读取输入并记录初始空白格位置 pair<int, int> st; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { cin >> math[i][j]; if (math[i][j] == 'x') { st.x = i; st.y = j; } } }
2. BFS初始化
queue<qj> q; qj start; start.arr = math; start.dep = 0; start.place = st; q.push(start); vis.insert(math);
3. 方向数组定义
定义四个移动方向(上、下、左、右):
int dx[4] = {0, 0, 1, -1}; // 行方向变化 int dy[4] = {1, -1, 0, 0}; // 列方向变化
4. BFS核心循环
while (!q.empty()) { auto a = q.front(); q.pop(); vector<vector<char>> now_arr = a.arr; int depth = a.dep; auto p = a.place; // 检查是否达到目标状态 if (now_arr == goal) { cout << depth << '\n'; return 0; } // 尝试四个方向的移动 for (int k = 0; k < 4; k++) { int nx = p.x + dx[k]; int ny = p.y + dy[k]; // 检查新位置是否合法 if (nx < 0 || nx >= 3 || ny < 0 || ny >= 3) continue; // 创建新状态并交换 vector<vector<char>> narr = now_arr; swap(narr[nx][ny], narr[p.x][p.y]); // 如果新状态未访问过,加入队列 if (vis.find(narr) == vis.end()) { vis.insert(narr); qj next; next.arr = narr; next.dep = depth + 1; next.place = {nx, ny}; q.push(next); } } }
算法流程
- 初始化:读取初始状态,记录空白格位置
- BFS准备:将初始状态加入队列和已访问集合
- 状态扩展:对于每个出队状态: 如果是目标状态,返回步数否则尝试四个方向的移动对于每个合法移动,生成新状态如果新状态未访问,加入队列
- 终止条件:找到目标状态或队列为空(无解)
复杂度分析
- 时间复杂度:O(9!) = O(362880),最坏情况下需要遍历所有可能状态
- 空间复杂度:O(9!),需要存储所有已访问状态