原题链接
①. 题目
②. 思路
- 负环:环路之和为负, 求最短路的时候会不断在负环路打转。
- 需要从每个点都出发一次,才能完全确定此图中是否有环
- 求负环的常用方法,基于spfa,一般都用方法 2
1:统计每个点入队的次数,如果某个点入队n次,则说明存在负环
2:统计当前每个点的最短路中所包含的边数,如果某点的最短路所包含的边数大于等于n,则也说明存在环
spfa判断步骤
1、dist[x] 记录虚拟源点到x的最短距离
2、cnt[x] 记录当前x点到虚拟源点最短路的边数,初始每个点到虚拟源点的距离为0,只要他能再走n步,即cnt[x] >= n,则表示该图中一定存在负环,由于从虚拟源点到x至少经过n条边时,则说明图中至少有n + 1个点,表示一定有点是重复使用
3、若dist[j] > dist[t] + w[i],则表示从t点走到j点能够让权值变少,因此进行对该点j进行更新,并且对应cnt[j] = cnt[t] + 1,往前走一步
4、该题是判断是否存在负环,并非判断是否存在从1开始的负环,因此需要将所有的点都加入队列中,更新周围的点
③. 学习点
④. 代码实现
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Main {
static int N=10010;
static int[] ne=new int[N],e=new int[N],h=new int[N],w=new int[N];
static int idx,n,m;
static boolean[] st=new boolean[N];
static int[] cnt=new int[N];
static int[] dist=new int[N];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n=sc.nextInt();m=sc.nextInt();
Arrays.fill(h,-1);
for (int i = 1; i<=m; i++) {
int a=sc.nextInt(),b=sc.nextInt(),c=sc.nextInt();
add(a,b,c);
}
if(spfa()) {
System.out.println("Yes");
}else{
System.out.println("No");
}
}
static void add(int a,int b,int c) {
e[idx]=b;
w[idx]=c;
ne[idx]=h[a];
h[a]=idx++;
}
static boolean spfa() {
Queue<Integer> q=new LinkedList<>();
for (int i = 1; i <=n; i++) {
q.offer(i);
}
while(!q.isEmpty()) {
int t=q.poll();
st[t]=false;
for(int i=h[t];i!=-1;i=ne[i]) {
int j=e[i];
if(dist[j]>dist[t]+w[i]) {
dist[j]=dist[t]+w[i];
cnt[j]=cnt[t]+1;
if(cnt[j]>=n) {
return true;
}
if(!st[j]) {
st[j]=true;
q.offer(j);
}
}
}
}
return false;
}
}