/* 改进的BellMan-Ford算法 SPFA
* 含有负权图的单源最短路径 最短路径如果存在,一定不含环
* 存在负环时最短路径不存在 当一个节点入队超过n次时说明图中存在负环
* 使用邻接表和队列实现 每次更新节点时只更新与之相关联的节点
* d[i]表示节点0到节点i的最短路径
* */
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;
public class BellmanFord {
static final int MAX = 1 << 30;
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int[] u = new int[m];// 边的起点
int[] v = new int[m];// 边的终点
int[] w = new int[m];// 边权
int[] first = new int[m];// frist[u]以u为起点的第一条边
Arrays.fill(first, -1);// 初始化表头
int[] next = new int[m];// next[i]编号为i的边的下一条边
int[] d = new int[n];
int[] count = new int[n];// 节点进队列的次数
Arrays.fill(d, MAX);
d[0] = 0;
int[] inq = new int[n];// 节点是否在队列中
int[] fa = new int[n];// 记录路径
boolean circle = false;
LinkedList<Integer> queue = new LinkedList<Integer>();
for (int e = 0; e < m; e++) {
u[e] = scanner.nextInt();
v[e] = scanner.nextInt();
w[e] = scanner.nextInt();
next[e] = first[u[e]];
first[u[e]] = e;
}
queue.offer(0);
count[0]++;
while (queue.size() > 0) {
int x = queue.poll();
inq[x] = 0;// 清除在队列中标识
for (int e = first[x]; e != -1; e = next[e]) {
if (d[v[e]] > d[x] + w[e]) {
d[v[e]] = d[x] + w[e];
fa[v[e]] = x;
if (inq[v[e]] == 0) {// 不在队列中才添加
if (count[v[e]] == n) {
System.out.println("存在负环");
circle = true;
return;
}
inq[v[e]] = 1;
count[v[e]]++;
queue.offer(v[e]);
}
}
}
}
if (!circle) {
for (int i = 0; i < n; i++) {
if (i == 0) {
System.out.println(0);
continue;
} else {
System.out.printf("%d<-", i);
int t = fa[i];
while (t > 0) {
System.out.printf("%d<-", t);
t = fa[t];
}
}
System.out.println(0 + " the minmum length is " + d[i]);
}
}
}
}
输入:
4
5
0 1 -1
1 2 -2
2 0 4
2 3 -3
0 3 1
输出
0
1<-0 the minmum length is -1
2<-1<-0 the minmum length is -3
3<-2<-1<-0 the minmum length is -6