Dijkstra算法求最短路径的图时不能有负权边,因为扩展到负权边的时候会产生更短的路径,有可就已经破坏了已经更新的点路程不会改变的性质。Dijkstra算法虽然好,具有良好的扩展性,扩展后可以适应很多问题,高效的A*算法就是有Dijkstra算法扩展来的,但Dijkstra算法无法解决负权边,这是最大的弊端。
Bellman-Ford算法正好弥补Dijkstra算法的这个弊端,可以堪称完美最短路径算法。
算法大致如下:每次选取队头顶点u,对顶点u的所有出边进行松弛操作。例如有一条边 u->v 的边,如果通过 u->v 这条边使得源点到顶点v的最短路径变短(dis[u]+e[u][v]<dis[v]),且顶点v不在当前的队列中,就将顶点v放入队尾。需要一个数组里判断哪些点已经在队列中。在对顶点u的所有出边松弛完毕后,就将顶点u出队。接下来不断从队列中取出新的队头顶点在进行如上操作,直至队列空为止。
代码:
#include <iostream>
#include <vector>
#include <queue>
const int MAX=999999;
int main()
{
int n, m; //n表示顶点个数,m表示边数
std::cin >> n >> m;
std::vector<int>u(m + 1); //起点集合
std::vector<int>v(m + 1); //末点集合
std::vector<int>w(m + 1); //权值集合
std::vector<int>first(n + 1); //存储首到尾号顶点的第一条边的编号
std::vector<int>next(m + 1); //存储当前编号下的边的下一条边的编号
std::vector<int>dis(n + 1); //存储顶点
std::vector<int>book(n + 1); //标记
std::queue<int>que; //维护队列
//初始化一波
for (int i = 1; i <= n; i++)
{
dis[i] = MAX;
book[i] = 0;
first[i] = -1;
}
dis[1] = 0; //初始化首顶点
for (int i = 1; i <= m; i++)
{
std::cin >> u[i] >> v[i] >> w[i];
//建立邻接表
next[i] = first[u[i]];
first[u[i]] = i;
}
que.push(1); //首号顶点入队
book[1] = 1;
while (!que.empty())
{
int k = first[que.front()]; //当前需要处理的队首顶点
//扫描当前顶点所有的边
while (k != -1)
{
if (dis[v[k]] > dis[u[k]] + w[k])
{
dis[v[k]] = dis[u[k]] + w[k];
if (book[v[k]] == 0)
{
que.push(v[k]);
book[v[k]] = 1;
}
}
k = next[k];
}
que.pop(); //出队
}
for (int i = 1; i <= n; i++)
{
std::cout << dis[i] << std::endl;
}
::getchar(); ::getchar();
return 0;
}