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来存,然后暴力标记一下走过的点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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(); } }
还有一种比A星算法更快的求k短路的算法:可持久化可并堆