Bellman-Ford && SPFA
Bellman-Ford
Bellman-Ford算法基于边求解最短路,解决单源最短路问题,可以处理含负权边的图,判断是否存在负环。
图上的一条最短路径不超过V-1条边,每次遍历在前一次的基础上操作,是一个迭代松弛的过程。可以把由源点出发到各点的最短路径表示成一棵树,外层的每一次遍历操作就是将树的深度加深一层,内层遍历边执行松弛操作。(松弛:dist[i] = min( dist[i], dist[j]+g[j][i] ))
步骤:
1、将dis(源点到各点的距离)t初始化为INF,dist[source] = 0;
2、外层循环V-1次,内层循环对当边的起点和当前边的终点进行松弛操作(最多执行V-1次)
3、进行第V次操作,如果仍出现有dist更新,则存在负环。
( 时间复杂度为O(V*E) )
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
struct edge{int from, to, cost;};
edge e[maxn];
int n, m;
int d[maxn];
int path[maxn];
bool Bellman_Ford(int s)
{
for(int i = 0; i < n; i++) d[i] = INF;
d[s] = 0;
for(int k = 0; k < n - 1; k++){
for(int i = 0; i < m; i++){
if(d[e[i].from] != INF && d[e[i].from] + e[i].cost < d[e[i].to]){
d[e[i].to] = d[e[i].from] + e[i].cost;
path[e[i].to] = e[i].from;
}
}
}
for(int i = 0; i < m; i++)
if(d[e[i].from] + e[i].cost < d[e[i].to])
return true;
return false;
}
int main()
{
int s;
while(~scanf("%d%d%d", &n, &m, &s)){
for(int i = 0; i < m; i++)
scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].cost);
if(Bellman_Ford(s)) printf("Exist negative loop\n");
else{
for(int i = 0; i < n; i++) printf("%d ", d[i]);printf("\n");
}
}
return 0;
}
SPFA
SPFA是对Bellman-Ford算法的优化。这里采用队列优化。
将存储结构优化为邻接表,使用队列操作代替外层所有点循环V-1次。判断负环的条件也就变为当某顶点进入队列超过V次,存在负环。
步骤:
1、初始化dist为INF,dist[source] = 0,源点source入队(isque标记点是否在队列内,vis统计点在队列中出现的次数)
2、当队列非空,每次取出队首元素,对于每一个队首所在的点与它可达的点进行松弛操作。若dist有更新,则将判断可达的点是否在队列内,若不在队列内,入队并统计出现次数。
3、当2中发现某一点出现此处大于V,则存在负环。
(期望时间复杂度为O(KE)、K为常数 ,最坏情况复杂度O(VE),可通过SLF和LLL策略对在队列中的插入方式进行优化)
这里采用了链式前向星的数据结构,相当于邻接表。
cnt是输入顺序的编号,head是当前以点x为起点的第一条边的编号,e.next是与当前边同起点的下一条边的位置
#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn = 100 + 10;
const int INF = 0x3f3f3f3f;
struct edge{int to, next, cost;};
edge e[maxn];
int head[maxn];
int dist[maxn];
int n, m, cnt;
void add_edge(int x, int y, int w)
{
e[cnt].to = y;
e[cnt].next = head[x];
e[cnt].cost = w;
head[x] = cnt;
cnt++;
}
bool spfa(int s)
{
for(int i = 0; i < n; i++) dist[i] = INF;
dist[s] = 0;
int vis[maxn]; memset(vis, 0, sizeof(vis));
bool inque[maxn]; memset(inque, false, sizeof(inque));
queue<int> q; q.push(s); vis[s]++; inque[s] = true;
while(!q.empty()){
int cur = q.front(); q.pop(); inque[cur] = false;
for(int i = head[cur]; ~i; i = e[i].next){
if(dist[e[i].to] > dist[cur] + e[i].cost){
dist[e[i].to] = dist[cur] + e[i].cost;
if(!inque[e[i].to]){
q.push(e[i].to); inque[e[i].to] = true;
if(++vis[e[i].to] > n) return false;
}
}
}
}
return true;
}
int main()
{
int s; cnt =0;
while(~scanf("%d%d%d", &n, &m, &s)){
memset(head, -1, sizeof(head));
for(int i = 0; i < m; i++){
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
add_edge(x, y, w);
}
if(!spfa(s)) printf("Exist negative loop!");
else{
for(int i = 0; i < n; i++)
printf("%d ",dist[i]);
printf("\n");
}
}
return 0;
}