洛谷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方向的移动和相应处理逻辑。
原创内容 转载请注明出处
