【图论】C006_洛谷模板题:负环(SPFA / Bellman-Ford / 链式向前星)

一、题目描述

寻找一个从顶点 1 所能到达的负环,负环定义为:一个边权之和为负的环。

输入格式

  • 本题有多组数据
  • 第一行一个正整数 TT 表示数据组数,对于每组数据:
  • 第一行两个正整数 N,M,表示图有 N 个顶点,M 条边

接下来 MM 行,每行三个整数 a, b, wa,b,w,表示a \rightarrow ba→b 有一条权值为 ww 的边(若 w<0w<0 则为单向,否则双向)。

输出格式

  • 共 T 行。对于每组数据,存在负环则输出一行 YES,否则输出一行 NO。

说明/提示

  • n 2000 m 3000 n≤2000,m≤3000
    10000 w 10000 −10000≤w≤10000 T 10 T≤10
2
3 4
1 2 2
1 3 4
2 3 1
3 1 -3
3 3
1 2 3
2 3 4
3 1 -8

NO
YES

二、题解

方法一:Bellman-Ford

题意:寻找一个从顶点 1 所能到达的负环。而常规的 Bellman-Ford 算法判断的是图中是否有负环,也就是说如果 1 号结点与负环不连通按理说我们应该输出 "NO"

加边错误以及疑惑::

  • 为什么之前加了 1 条边会得 0 分,现在如果 cost >= 0 需要再加 1 条平行边就能的 72 分?
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E;
	static int[] dist, count;
	static boolean[] inq;
	static List<Edge>[] graph;
	static int INF = 0x3f3f3f3f;
	
	private static List<Edge>[] createGraph(int V){
	    List<Edge>[] graph = new List[V+1];
		for (int i = 0; i <= V; i++) 
			graph[i] = new ArrayList<>();
		return graph;
	}
	private static void addEdge(int from, int to, int cost) {
		graph[from].add(new Edge(to, cost));
	}
	private static boolean spfa(int S) {
		Queue<Integer> q = new ArrayDeque<>();
		q.offer(S);
		inq[S] = true;
        dist[S] = 0;		
        for (int i = 1; i <= V; i++) {
			q.add(i);
			inq[i] = true;
		}
		while (!q.isEmpty()) {
			Integer cur = q.poll();
			inq[cur] = false;
			for (int i = 0; i < graph.length; i++)
			for (Edge edge : graph[i]) {
				if (dist[edge.to] > dist[i] + edge.cost) {
					dist[edge.to] = dist[i] + edge.cost;
					count[edge.to] = count[i] + 1;
					if (count[edge.to] >= V)
					    return true;
					if (!inq[edge.to]) {
						q.offer(edge.to);
						inq[edge.to] = true;
					}
				}
			}
		}
		return false;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
        int T = sc.nextInt();
		while (T-- > 0) {
			V = sc.nextInt();
			E = sc.nextInt();
			inq = new boolean[V+1];
			graph = createGraph(V+1);
			dist = new int[V+1];
			count = new int[V+1];
			
			for (int i = 0; i < E; i++) {
				int from = sc.nextInt();
				int to = sc.nextInt();
				int cost = sc.nextInt();
				addEdge(from, to, cost);
				if (cost >= 0)
					addEdge(to, from, cost);
			}
			System.out.println(spfa(1) ? "YES" : "NO");
		}
    }
	static class Edge {
		int to, cost;
		public Edge(int _to, int _cost) {
			to = _to;
			cost = _cost;
		}
	}
}

复杂度分析

  • 时间复杂度: O ( E V ) O(EV)
  • 空间复杂度: O ( E + V ) O(E+V)

方法二:链式前向星

边界出错,哭了:

  • 现在一般的内存限制都够用的,所以直接定义 MAXN = 上限 即可,不用取考虑边界问题。
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E;
	static int[] dist, count;
	static int INF = 0x3f3f3f3f;
	static boolean[] inq;
	static int[] head;
	static Edge[] edges;
	static int tot;
	static int MAXN = 100000;
	private static void addEdge(int u, int v, int w) {
	    edges[tot] = new Edge();
		edges[tot].to = v;
		edges[tot].w = w;
		edges[tot].next = head[u];
		head[u] = tot++;
	}
	private static boolean spfa(int S) {
	    dist[S] = 0;
	    Queue<Integer> q = new ArrayDeque<>();
	    q.add(S);
	    inq[S] = true;
	    
	    while (!q.isEmpty()) {
	        int v = q.poll();
	        inq[v] = false;
	        for (int i = head[v]; i != 0; i = edges[i].next) {
				int to = edges[i].to, w = edges[i].w;
				if (dist[to] > dist[v] + w) {
					count[to] = count[v] + 1;
					if (count[to] >= V)
						return true;
					dist[to] = dist[v] + w;
					if (!inq[to]) {
						q.add(to);
						inq[to] = true;
					}
				}
			}
	    }
		return false;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		int T = sc.nextInt();
		while (T-- > 0) {
			V = sc.nextInt();
			E = sc.nextInt();
			inq = new boolean[V+1];
			dist = new int[V+1];
			count = new int[V+1];
			head = new int[V+1];
			edges = new Edge[MAXN];
			Arrays.fill(dist, INF);
			
			for (int i = 0; i < E; i++) {
				int from = sc.nextInt();
				int to = sc.nextInt();
				int cost = sc.nextInt();
				addEdge(from, to, cost);
				if (cost >= 0) {
				    addEdge(to, from, cost);
				}
					
			}
			System.out.println(spfa(1) ? "YES" : "NO");
		}
    }
	static class Edge {
		int to, w, next;
		public Edge() {		}
		public Edge(int to, int w, int next) {
			this.to = to;
			this.w = w;
			this.next = next;
		}
	}
}

复杂度分析

  • 时间复杂度: O ( k E ) O(kE)
  • 空间复杂度: O ( V + E ) O(V+E)
发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105487641
今日推荐