【图论】C006_模板题:畅通工程(SPFA / Dijkstra / )

一、题目描述

某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。

Input

  • 本题目包含多组数据,请处理到文件结束。
  • 每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
  • 接下来是M行道路信息。每一行有三个整 A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路
  • 再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。

Output

  • 对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.
Sample Input
3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2

Sample Output
2 -1

二、题解

方法一:双边 spfa

题目说 A 与 B 城市存在双向道路,所以使用邻接表的时候,需要加双边。由于不存在负环,如果边是点的两倍的话,用 spfa 效率会降低到 O ( E V ) O(EV)

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int[] d;
	static boolean[] inq;
	static List<Edge>[] G;
	static int INF = 0x3f3f3f3f;
	
	static int spfa(int s, int e) {
		Queue<Integer> q = new ArrayDeque<>();
		Arrays.fill(d, INF);
		d[s] = 0;
		q.add(s);
		inq[s] = true;
		
		while (!q.isEmpty()) {
			Integer now = q.poll();
			inq[now] = false;
			for (int i = 0; i < G.length; i++) {
				for (Edge edge : G[i]) {
					if (d[edge.to] > d[i] + edge.cost) {
						d[edge.to] = d[i] + edge.cost;
						if (inq[edge.to]) continue;
						q.add(edge.to);
						inq[edge.to] = true;
					}
				}
			}
		}
		if (d[e] > INF / 2) return -1;
		return d[e];
	}
	
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		while (sc.hasNext()) {
			int V = sc.nextInt();
			int E = sc.nextInt();
			inq = new boolean[V+1];
			d = new int[V+1];
			G = new List[V+1];
			for (int i = 0; i <= V; i++) {
			    G[i] = new ArrayList<Edge>();
			}
			for (int i = 0; i < E; i++) {
				int a = sc.nextInt();
				int b = sc.nextInt();
				int c = sc.nextInt();
				G[a].add(new Edge(b, c));
				G[b].add(new Edge(a, c));
			}
			int s = sc.nextInt();
			int e = sc.nextInt();
			System.out.println(spfa(s, e));
		}
    }
	static class Edge {
		int to;
		int cost;
		public Edge(int to, int cost) {
		  this.to = to;
		  this.cost = cost;
		}
	}
}

复杂度分析

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

方法二:Dijkstra 算法

Dijkstra 算法适用于无负权图的单源最短路径问题,该算法使用一个优先队列来存储离源点最近的新节点,避免了每次遍历点的边集时,都去枚举不必要的点来更新最短路径 dist[i]

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int[] d;
	static boolean[] inq;
	static List<Edge>[] G;
	static int INF = 0x3f3f3f3f;
	
	static int dijkstra(int s, int e) {
		Queue<Edge> q = new PriorityQueue<>((e1, e2) -> e1.cost - e2.cost);
		Arrays.fill(d, INF);
		d[s] = 0;
		q.add(new Edge(s, 0));
		
		while (!q.isEmpty()) {
			Edge now = q.poll();
			for (Edge edge : G[now.to]) {
				if (d[edge.to] > d[now.to] + edge.cost) {
					d[edge.to] = d[now.to] + edge.cost;
					q.add(new Edge(edge.to, d[edge.to]));
				}
			}
		}
		if (d[e] > INF / 2) return -1;
		return d[e];
	}
	
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		while (sc.hasNext()) {
			int V = sc.nextInt();
			int E = sc.nextInt();
			inq = new boolean[V+1];
			d = new int[V+1];
			G = new List[V+1];
			for (int i = 0; i <= V; i++) {
			    G[i] = new ArrayList<Edge>();
			}
			for (int i = 0; i < E; i++) {
				int a = sc.nextInt();
				int b = sc.nextInt();
				int c = sc.nextInt();
				G[a].add(new Edge(b, c));
				G[b].add(new Edge(a, c));
			}
			int s = sc.nextInt();
			int e = sc.nextInt();
			System.out.println(dijkstra(s, e));
		}
    }
	static class Edge {
		int to;
		int cost;
		public Edge(int to, int cost) {
		  this.to = to;
		  this.cost = cost;
		}
	}
}

复杂度分析

  • 时间复杂度: O ( E   l o g V ) O(E\ logV) ,队列中最多存在 V 个顶点,需要取枚举每一条边。
  • 空间复杂度: O ( E + V ) O(E+V)

题目链接:这里

发布了691 篇原创文章 · 获赞 151 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/105484784