bellman-ford
首先,如果最短路存在,那么一定有一条不含环的最短路,因为如果是正环或零环,都可以直接去除,如果有负环,则最短路不存在,所以最短路顶多经过n-1个顶点,那么我们至多只需要进行n-1次松弛操作,每次操作中遍历所有边,如果该边的起点不是INF(已经松弛过)那么就对该边的终点松弛。这样就一定可以把最短路经过的所有点都松弛一遍,即求出了最短路(因为只要可以松弛,就一定有更短的方案)。
//bellmanford
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1000 + 10;
const int INF = 0x3f3f3f3f;
int u[MAXN], v[MAXN], w[MAXN];
int d[MAXN];
int n, m;
int main()
{
freopen("sp.in", "r", stdin);
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
cin >> u[i] >> v[i] >> w[i];
u[i+m] = v[i], v[i+m] = u[i], w[i+m] = w[i];
}
d[1] = 0;
for(int i = 2; i <= n; i++) d[i] = INF;
for(int k = 1; k < n; k++)
for(int i = 1; i <= 2*m; i++)
if(d[u[i]] < INF) d[v[i]] = min(d[v[i]], d[u[i]] + w[i]);
for(int i = 1; i <= n; i++)
cout << d[i] << " ";
return 0;
}
SPFA
思路:
- 把除起点外的所有点距离设为无限,然后让起点进入队列
- 每次取出队列的第一个点,讨论他的所有只向点进行松弛操作,如果松弛成功并且该点并不在队列中,将该点放入队列,直到队列中没有点为止
和dijkstra的比较
- dijkstra每次取出的是当前距离最小点,在后面的讨论中不可能再更新该点的值,所以每个点只用讨论一次
- SPFA每次取出的只是一个松弛过的点,很有可能再次讨论回来,所以一个点要讨论多次
- 但是因为dijkstra中每次找距离最小点需要时间复杂度,所以O(mlogn)
- 而SPFA没有这个需要,所以O(kn),k是常数,而一般不会超过2
- 而且dijkstra只能解决没有负权的问题,而SPFA可以解决,但不能解决有负环的问题,因为有负环的图没有最短路,他还可以判定负环,当一个点的进队次数超过n后,可以判定图中有负环
下面是SPFA的代码
//SPFA
#include<cstdio>
#include<cstdlib>
#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10000 + 10;
const int INF = 0x3f3f3f3f;
queue<int> q;
int n, m;
int d[MAXN];
int vis[MAXN];
struct edge
{
int to, next, w;
}e[MAXN];
int head[MAXN], cnt;
void add(int u, int v, int w)
{
e[++cnt].next = head[u];
head[u] = cnt;
e[cnt].to = v;
e[cnt].w = w;
}
int main()
{
freopen("sp.in", "r", stdin);
cin >> n >> m;
for(int i = 1; i <= m; i++)
{
int u, v, w;
cin >> u >> v >> w;
add(u, v, w); add(v, u, w);
}
for(int i = 2; i <= n; i++)
d[i] = INF;
q.push(1); vis[1] = 1;
while(!q.empty())
{
int u = q.front(); q.pop(); vis[u] = 0;
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(d[v] > d[u] + e[i].w)
{
d[v] = d[u] + e[i].w;
if(!vis[v]) {q.push(v); vis[v] = 1;}
}
}
}
for(int i = 1; i <= n; i++)
cout << d[i] << " ";
return 0;
}