带有负权边的最短路径解法
前面写了一下Dijkstra算法求解单源最短路,但是该算法对于带有负权边时,是不适用的。下面来搞下其他的两种算法。Bellman-Ford算法,是一个无论思想上还是代码实现上都堪称完美的最短路算法。
核心代码只有4行
for(int k = 1; k <= n - 1; k ++)
for(int i = 1; i <= m; i ++)
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i];
完整代码如下:
#include<iostream>
#include<cstdio>
#define maxPath 99999999
using namespace std;
int n,m;
int u[1001],v[1001],w[1001],dis[1001];
int main()
{
cin >> n >> m;
//读入m条边
for(int i = 1; i <= m; i ++)
cin >> u[i] >> v[i] >> w[i];
//初始化dis
for(int i = 1; i <= n; i ++)
dis[i] = maxPath;
dis[1] = 0;
for(int i = 1; i < n; i ++)
for(int j = 1; j <= n; j ++)
if(dis[v[j]] > dis[u[j]] + w[j])
dis[v[j]] = dis[u[j]] + w[j];
for(int i = 1; i <= n; i ++)
cout << dis[i] << " ";
return 0;
}
要注意的是该算法可能不需要n-1松弛,可以在循环时放入check函数进行判断路径是否更新,若没有更新,则可以退出循环。
下面的一种算法是Bellman-Ford的队列优化算法。
先看下建立邻接表代码
for(int i = 1; i <=m; i ++)
{
//读入每一条边
scanf("%d%d%d",&u[i],&v[i],&w[i]);
next[i]= first[u[i]];
first[u[i]] = i;
}
next[i]里面存储的是u[i]节点为起点的上一条边的编号
first里面存储的是最新的一条边编号
该算法完整代码如下:
#include<iostream>
#include<cstdio>
#define maxPath 99999999
using namespace std;
int n,m;
int u[1001],v[1001],w[1001],dis[1001];
int book[1001];
int que[1001],head = 1,tail = 1;
int first[1001],next[1001];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i ++)
dis[i] = maxPath;
dis[1] = 0;
for(int i = 1; i <= n; i ++)
first[i] = -1;
for(int i = 1; i <= m; i ++)
{
scanf("%d%d%d",&u[i],&v[i],&w[i]);
next[i] = first[u[i]];
first[u[i]] = i;
}
que[head] = 1;
tail ++;
book[1] = 1;
int k = 0;
while(head < tail)
{
k = first[que[head]];
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[tail] = v[k];
tail ++;
book[v[k]] = 1;
}
}
k = next[k];
}
//book[que[head]] = 0;
head ++;
}
for(int i = 1; i <= n; i ++)
cout << dis[i] << " ";
return 0;
}
这也是队列的一种思想,想从源点开始找可以到达的点,再将路径更新,head++,重复此过程。