BFS算法实战:洛谷P2346棋盘游戏最短步数解法详解与C++实现
一、问题描述与解题思路
洛谷P2346题目要求我们计算在一个4x4的棋盘上,通过移动棋子使得四个相同颜色的棋子连成一条直线(横、竖或对角线)所需的最少步数。棋盘上有黑色(B)、白色(W)棋子和空格(O),每次只能将相邻的自己颜色的棋子移动到相邻的空格。
二、完整代码实现(带详细注释)
#include <iostream> #include <queue> #include <unordered_map> #include <string> #include <algorithm> using namespACe std; // 定义棋盘状态结构体 struct State { string board; // 棋盘状态字符串 int steps; // 到达该状态的步数 bool isBlack; // 当前该谁走棋 }; // 检查是否达到目标状态(四个一线) bool isGoal(const string& board) { // 检查所有行 for (int i = 0; i < 4; ++i) { if (board[i*4] != 'O' && board[i*4] == board[i*4+1] && board[i*4] == board[i*4+2] && board[i*4] == board[i*4+3]) return true; } // 检查所有列 for (int i = 0; i < 4; ++i) { if (board[i] != 'O' && board[i] == board[i+4] && board[i] == board[i+8] && board[i] == board[i+12]) return true; } // 检查对角线 if (board[0] != 'O' && board[0] == board[5] && board[0] == board[10] && board[0] == board[15]) return true; if (board[3] != 'O' && board[3] == board[6] && board[3] == board[9] && board[3] == board[12]) return true; return false; } // 获取所有可能的下一步状态 vector<State> getNextStates(const State& current) { vector<State> nextStates; const string& board = current.board; char currentPlayer = current.isBlack ? 'B' : 'W'; // 找到所有空格位置 vector<int> emptyPos; for (int i = 0; i < 16; ++i) { if (board[i] == 'O') emptyPos.push_back(i); } // 对于每个空格,检查四周可以移动过来的棋子 for (int pos : emptyPos) { int row = pos / 4; int col = pos % 4; // 检查四个方向 int dirs[4][2] = {{-1,0}, {1,0}, {0,-1}, {0,1}}; for (auto& dir : dirs) { int newRow = row + dir[0]; int newCol = col + dir[1]; if (newRow >= 0 && newRow < 4 && newCol >= 0 && newCol < 4) { int newPos = newRow * 4 + newCol; if (board[newPos] == currentPlayer) { // 可以移动这个棋子到空格 string newBoard = board; swap(newBoard[newPos], newBoard[pos]); nextStates.push_back({newBoard, current.steps + 1, !current.isBlack}); } } } } return nextStates; } int bfs(const string& initialBoard) { queue<State> q; unordered_map<string, bool> visited; // 初始状态,黑方先走 q.push({initialBoard, 0, true}); // 白方先走的初始状态 q.push({initialBoard, 0, false}); visited[initialBoard + "1"] = true; // 黑方先走的标记 visited[initialBoard + "0"] = true; // 白方先走的标记 while (!q.empty()) { State current = q.front(); q.pop(); if (isGoal(current.board)) { return current.steps; } vector<State> nextStates = getNextStates(current); for (const State& next : nextStates) { string key = next.board + (next.isBlack ? "1" : "0"); if (!visited.count(key)) { visited[key] = true; q.push(next); } } } return -1; // 无解情况 } int main() { string board; string line; // 读取4x4棋盘 for (int i = 0; i < 4; ++i) { cin >> line; board += line; } int result = bfs(board); cout << result << endl; return 0; }
三、算法核心解析
状态表示:
使用字符串表示棋盘状态,便于存储和比较
State结构体记录当前棋盘、步数和轮到哪方走棋
BFS搜索过程:
目标检测:
检查所有行、列和对角线是否有四个相同非空格棋子
一旦发现目标状态立即返回当前步数
避免重复搜索:
使用哈希表记录已访问状态
状态键包含棋盘和当前玩家信息
四、复杂度分析与优化建议
最坏情况下需要遍历所有可能状态
实际运行时间取决于棋盘的初始布局
空间复杂度:
需要存储所有已访问状态
对于4x4棋盘,状态空间在可控范围内
优化建议:
可以考虑双向BFS优化
使用更高效的状态哈希方法
对于特定模式可以提前终止搜索
原创内容 转载请注明出处