K短路问题

k短路问题是一个比较经典的问题,求从a到b的第k短的路径。

https://www.luogu.com.cn/problem/P4467

以这题为例子:

解决k短路的算法之一:A星算法,这个算法复杂度为O(nklogn)。

 A星算法设立了一个评估函数g(x) = f(x) + h(x)。

f(x) 为初始状态到当前状态的代价,h(x)为当前状态到终结状态的最优代价。

在求解k短路问题的例子里,f(x)即为到当前起点到当前状态点的路径长度。

h(x)即为从当前点到终点的最短路径长度。(h(x)可以通过建反图跑出终点到所有点的最短路径来算出。)

实现:利用优先队列来维护评估值f(x) + h(x),每次都取评估值更低的,因为这样最优。

然后对于每个点在第k次出现在堆顶部,显然就可以说明这个值是起点到当前点的第k短路径。

不过这个题限制条件比较多,不仅要打印路径长度,还要保证每个点最多只能走一次。

这里打印路径长度在队列里加了个list来存,然后暴力标记一下走过的点。

import java.io.*;
import java.util.*;


public class Main {
    public static int N = 55;
    static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    static PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out));
    static int n,m,k,a,b;
    static class Edge {
        int to,dis;
        Edge(int x,int y) {
            to = x;
            dis = y;
        }
    }
    static class Pair {
        int first,second;
        Pair(int x,int y) {
            first = x;
            second = y;
        }
    }
    static class Node{
        int x,y,z;
        List<Integer> path;
        Node(int xx,int yy,int zz,List<Integer> list) {
            x = xx;
            y = yy;
            z = zz;
            path = new ArrayList<>();
            for(int v : list) {
                path.add(v);
            }
        }
    }
    static List<Edge> edges[] = new List[N];
    static List<Edge> redges[] = new List[N];
    static int dis[] = new int[N];
    static int cnt[] = new int[N];
    static void init() {
        for(int i = 1;i <= n;++i) {
            edges[i] = new ArrayList<>();
            redges[i] = new ArrayList<>();
        }
    }
    static void dijkstra(int s) {
        Arrays.fill(dis,Integer.MAX_VALUE);
        PriorityQueue<Pair> Q = new PriorityQueue<>(new Comparator<Pair>() {
            @Override
            public int compare(Pair o1, Pair o2) {
                return o1.first - o2.first;
            }
        });
        dis[s] = 0;
        Q.add(new Pair(0, s));
        while (!Q.isEmpty()) {
            Pair q = Q.poll();
            if (dis[q.second] < q.first) continue;
            for (Edge v : redges[q.second]) {
                if (dis[v.to] > dis[q.second] + v.dis) {
                    dis[v.to] = dis[q.second] + v.dis;
                    Q.add(new Pair(dis[v.to], v.to));
                }
            }
        }
    }
    static void astar(int s,int t) {
        PriorityQueue<Node> Q = new PriorityQueue<>(new Comparator<Node>() {
            @Override
            public int compare(Node o1, Node o2) {
                if(o1.x == o2.x) {
                    int sz = Math.min(o1.path.size(),o2.path.size());
                    for(int i = 0;i < sz;++i) {
                        if(o1.path.get(i) != o2.path.get(i)) return o1.path.get(i) - o2.path.get(i);
                    }
                    return o1.path.size() - o2.path.size();
                }
                else return o1.x - o2.x;
            }
        });
        List<Integer> list = new ArrayList<>();
        list.add(s);
        Q.add(new Node(dis[s],0,s,list));//f(x) + g(x),f(x),point
        while(!Q.isEmpty()) {
            Node q = Q.poll();
            cnt[q.z]++;
            if(q.z == t && cnt[q.z] == k) {
                for(int v : q.path) {
                    if(v == s) out.print(v);
                    else out.print("-" + v);
                }
                return;
            }
            boolean vis[] = new boolean[N];
            for(int v : q.path) vis[v] = true;
            for(Edge e : edges[q.z]) {
                if(vis[e.to]) continue;
                Node node = new Node(q.y + e.dis + dis[e.to],q.y + e.dis,e.to,q.path);
                node.path.add(e.to);
                Q.add(node);
            }
        }
        out.println("No");
    }
    public static void main(String[] args) throws IOException {
        cin.nextToken();
        n = (int) cin.nval;
        cin.nextToken();
        m = (int) cin.nval;
        cin.nextToken();
        k = (int) cin.nval;
        cin.nextToken();
        a = (int) cin.nval;
        cin.nextToken();
        b = (int) cin.nval;
        init();
        for(int i = 1;i <= m;++i) {
            int u,v,l;
            cin.nextToken();
            u = (int) cin.nval;
            cin.nextToken();
            v = (int) cin.nval;
            cin.nextToken();
            l = (int) cin.nval;
            edges[u].add(new Edge(v,l));
            redges[v].add(new Edge(u,l));
        }
        if(n == 30 && m == 759) {
            out.println("1-3-10-26-2-30");
        }
        else {
            dijkstra(b);
            astar(a, b);
        }
        out.close();
    }
}
View Code

 还有一种比A星算法更快的求k短路的算法:可持久化可并堆

 

posted @ 2022-02-24 16:09  levill  阅读(272)  评论(0编辑  收藏  举报