【图论】B_算竞_第 K 短路(A*)

一、题目描述

给定一张 N 个点(编号1,2…N),M条边的有向图,求从起点S到终点T的第K短路的长度,路径允许重复经过点或边。

注意: 每条最短路中至少要包含一条边。

输入格式

  • 第一行包含两个整数N和M。
  • 接下来M行,每行包含三个整数A,B和L,表示点A与点B之间存在有向边,且边长为L。
  • 最后一行包含三个整数S,T和K,分别表示起点S,终点T和第K短路。

输出格式

  • 输出占一行,包含一个整数,表示第K短路的长度,如果第K短路不存在,则输出“-1”。

数据范围

  • 1≤S,T≤N≤1000,
    0≤M≤105,
    1≤K≤1000,
    1≤L≤100
输入样例:
2 2
1 2 5
2 1 4
1 2 2
输出样例:
14

二、题解

方法一:Dijkstra

Q&A:

  • Q1:如果图中终点自环,怎么处理?比如在下面这个数据终点自环,那么有可能第 k 短路不是正常所求,如果解决?
    A1:
    4 10
    2 3 72
    4 4 13
    4 3 36
    2 3 12
    2 1 74
    1 2 12
    3 1 48
    4 2 58
    2 2 24
    4 1 79
    3 3 4
    
import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
    static int V, E;
    static int INF = 0x3f3f3f3f;
    static int MAXN = (int)1e5 + 50;
    static Edge[] edges;
    static int[] head;
    static int tot;
    static boolean[] inq;
	static int cnt;
	static int[] d;
	
    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;
    }
    static void dijkstra(int s, int e, int k) {
        Arrays.fill(d, INF);
		Queue<Node> q = new PriorityQueue<>((e1, e2) -> (e1.d - e2.d));
		q.add(new Node(1, 0));
        while (!q.isEmpty()) {
            Node t = q.poll();
			if (t.id == e) {
				d[++cnt] = t.d;
				if (cnt == k+1)
					return;
			}
            for (int i = head[t.id]; i != 0; i = edges[i].next) {
				int to = edges[i].to, w = edges[i].w;
				q.add(new Node(to, w + t.d));
            }
        }
    }
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));

        V = sc.nextInt();
        E = sc.nextInt();
        inq = new boolean[MAXN];
        edges = new Edge[MAXN];
        head = new int[MAXN];
        d = new int[MAXN];
        for (int i = 1; i <= E; i++) {
            int a = sc.nextInt();
            int b = sc.nextInt();
            int c = sc.nextInt();
            addEdge(a, b, c);
        }
		int s = sc.nextInt();
		int e = sc.nextInt();
		int k = sc.nextInt();
		
		dijkstra(s, e, k);
	    System.out.println(d[k] == INF || cnt <= k ? -1 : d[k]);
    }
	static class Node {
		int id, d;
		Node(int id, int d) {
			this.id = id;
			this.d = d;
		}
	}
    static class Edge {
        int to, w, next;
        Edge() {}
    } 
}

复杂度分析

  • 时间复杂度: O ( E l o g V ) O(E logV)
  • 空间复杂度: O ( . . . ) O(...)

方法二:A*

关于 A* 的介绍,看这里,这里简单说一下,A* 其实是一种在最短路基础之上通过启发函数进行估计最优走向的一种算法。

细节:因为最短路包含至少一条路径,所以当 S = E 时,我们如果求第一短路,那么我们的算法会返回 dist[1] == 0,这是不对的,所以当 S = E 时,我们选择求第 K+1 短路。

import java.util.*;
import java.math.*;
import java.io.*;
public class Main{
	static int V, E, K, T;
	static int[] dist1, head1, head2;
	static Edge[] e1, e2;
	static int maxv = 1000 + 50, maxe = (int) 1e5 + 50;
	static int tot1, tot2;
	static int INF = 0x3f3f3f3f;
	
	static void addEdge1(int u, int v, int w) {
		e1[++tot1] = new Edge();
		e1[tot1].to = v;
		e1[tot1].w = w;
		e1[tot1].next = head1[u];
		head1[u] = tot1;
	}
	static void addEdge2(int u, int v, int w) {
		e2[++tot2] = new Edge();
		e2[tot2].to = v;
		e2[tot2].w = w;
		e2[tot2].next = head2[u];
		head2[u] = tot2;
	}
	static void dijkstra(int S) {
		boolean[] vis = new boolean[maxv];
		Arrays.fill(dist1, INF);
		dist1[S] = 0;
		Queue<Node> q = new PriorityQueue<>((e1, e2) -> e1.cost - e2.cost);
		q.add(new Node(S, dist1[S]));
		
		while (!q.isEmpty()) {
			Node now = q.poll();
			int v = now.v;
			if (vis[v])
				continue;
			vis[v] = true;
			for (int i = head1[v]; i != 0; i = e1[i].next) {
				int to = e1[i].to, w = e1[i].w;
				if (dist1[to] > dist1[v] + w) {
					dist1[to] = dist1[v] + w;
					if (!vis[to]) {
						q.add(new Node(to, dist1[to]));
						// vis[to] = true; 注,无需
					}
				}
			}
		}
	}
	static int f(int v) {
		return dist1[v];
	}
	static int A(int S) {
		if (dist1[S] == INF)	  //如果起点与终点不连通
			return -1;
		int[] cnt = new int[maxv];
		boolean[] vis = new boolean[maxv];
		Queue<Node> pq = new PriorityQueue<>((n1, n2) -> (n1.cost + f(n1.v)) - (n2.cost + f(n2.v)));
		pq.add(new Node(S, 0));
		
		while (!pq.isEmpty()) {
			Node t = pq.poll();
			if (vis[t.v]) 
				continue;
			int v = t.v, cost = t.cost;
			cnt[v]++;
			if (v == T && cnt[v] == K)
				return cost;
			for (int i = head2[v]; i != 0; i = e2[i].next) {
				int to = e2[i].to, w = e2[i].w;
				pq.add(new Node(to, cost + w));
			}
		}
		return -1;
	}
    public static void main(String[] args) throws IOException {  
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        BufferedWriter w = new BufferedWriter(new OutputStreamWriter(System.out));
        
		V = sc.nextInt();
		E = sc.nextInt();
		e1 = new Edge[maxe];   e2 = new Edge[maxe];
		head1 = new int[maxv]; head2 = new int[maxv];
		dist1 = new int[maxv];
		for (int i = 0; i < E; i++) {
			int a = sc.nextInt();
			int b = sc.nextInt();
			int c = sc.nextInt();
			addEdge1(b, a, c);
			addEdge2(a, b, c);
		}
		int S = sc.nextInt();
		T = sc.nextInt();
		K = sc.nextInt();
		if (S == T) 
			K++;
		dijkstra(T);
		int res = A(S);
		System.out.println(res);
    }
	static class Node {
		int v, cost; 
		Node(int v, int cost) {
		    this.v = v;
		    this.cost = cost;
		}
	}
	static class Edge {
		int to, w, next;
		Edge(){}
	}
}

复杂度分析

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

猜你喜欢

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