洛谷P4554题终极指南:双端队列BFS解决网格图最短路径问题 | 算法新手必备
一、问题理解与算法思路
题目要求在给定的网格图中,从起点到终点找到一条路径,使得经过不同字符的边数最少。这道题考察了两个核心算法点:广度优先搜索(BFS)和双端队列优化。我们采用双端队列优化的BFS算法,既保证了正确性又提高了效率。
解题关键步骤:
二、完整代码实现(带详细注释)
#include <iostream> #include <deque> #include <cstring> using namespACe std; const int N = 510; char grid[N][N]; // 存储网格图 int dist[N][N]; // 存储到每个点的最短距离 int dx[] = {-1, 1, 0, 0}; // 四个方向的x偏移量 int dy[] = {0, 0, -1, 1}; // 四个方向的y偏移量 int n, m, x1, y1, x2, y2; // 网格大小和起点终点坐标 int bfs() { memset(dist, 0x3f, sizeof dist); // 初始化距离为极大值 deque<pair<int, int>> q; // 双端队列 q.push_back({x1, y1}); dist[x1][y1] = 0; // 起点距离为0 while (!q.empty()) { auto t = q.front(); q.pop_front(); // 遍历四个方向 for (int i = 0; i < 4; i++) { int nx = t.first + dx[i], ny = t.second + dy[i]; if (nx < 1 || nx > n || ny < 1 || ny > m) continue; // 越界检查 // 计算移动成本:如果字符不同则成本为1,否则为0 int cost = (grid[nx][ny] != grid[t.first][t.second]); if (dist[nx][ny] > dist[t.first][t.second] + cost) { dist[nx][ny] = dist[t.first][t.second] + cost; // 根据成本决定插入队列的位置 if (cost == 0) q.push_front({nx, ny}); // 成本0插入队首 else q.push_back({nx, ny}); // 成本1插入队尾 } } } return dist[x2][y2]; // 返回终点的最短距离 } int main() { while (cin >> n >> m, n || m) { // 处理多组输入直到n和m都为0 // 读取网格图 for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> grid[i][j]; cin >> x1 >> y1 >> x2 >> y2; x1++, y1++, x2++, y2++; // 转换为1-based索引 cout << bfs() << endl; } return 0; }
三、算法核心解析
双端队列优化:根据移动成本决定节点插入位置,0成本插入队首,1成本插入队尾
距离松弛:只有当找到更短路径时才更新距离并重新入队
边界处理:正确处理网格边界防止越界访问
多组输入:使用while循环处理多组测试数据
四、复杂度分析与优化
时间复杂度:O(nm),每个节点最多被处理一次
空间复杂度:O(nm),存储距离矩阵和队列
优化建议:
可使用优先队列实现Dijkstra算法
可以尝试A*算法等启发式搜索
五、常见问题解答
Q:为什么使用双端队列而不是普通队列? A:双端队列可以保证0成本的移动优先处理,类似于优先级队列但常数更小。
Q:如何处理更大的网格? A:可以考虑使用更高效的数据结构如跳表,或者优化内存访问模式。
Q:算法是否可以扩展到三维网格? A:可以,只需增加z方向的移动和相应处理逻辑。
原创内容 转载请注明出处