第k短路(Dijkstra & A*)

最短路,即第1短路有很多种求法,SPFA,Dijkstra等,但第k短路怎么求呢?其实也是基于Dijkstra;因为Dijkstra用的是堆优化,这样保证每次弹出来的都是最小值,只是求最短路只是弹出一次就返回了,我们可以用Dijkstra弹出k个距离后再返回,这样根据弹出的先后顺序能够求出1~k短路

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e5 + 100;
const int MAXM = 3e3 + 10;

inline int read() {
    int x = 0, ff = 1; char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') ff = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * ff;
}

inline void write(ll x) {
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
}

int n, m, k, v;
ll ans, dist[110];
int lin[MAXN], tot = 0;
struct edge {
    int y, v, next;
}e[MAXN];

inline void add(int xx, int yy, int vv) {
    e[++tot].y = yy;
    e[tot].v = vv;
    e[tot].next = lin[xx];
    lin[xx] = tot;
}

void Dijkstra() {
    priority_queue < pair < int , int > > q;
    q.push(make_pair(0, 1));
    while(!q.empty()) {
        int x = q.top().second;
        int d = -q.top().first;
        q.pop();    
        if(x == n)  {
            dist[++v] = d;
            if(v == k + 1) return ;
        }
        for(int i = lin[x], y; i; i = e[i].next) {    
            y = e[i].y;
            ans = d + e[i].v;
            q.push(make_pair(-ans, y));
        }
    }
}

int main() {
    memset(dist, -1, sizeof(dist));
    n = read(); m = read(); k = read();
    for(int i = 1; i <= m; ++i) {
        int x,y,v;
        x = read(); y = read(); v = read();
        add(x, y, v);
    }
    Dijkstra();
    for(int i = 1; i <= k; ++i) {
        write(dist[i]);
        putchar('\n');
    }
    return 0;
}

 

 

emmmmm, 还有一种更高级的算法, 先来回顾优先队列的BFS, 不断从堆中取出“当前代价最小” 的状态进行拓展。每个状态第一次从堆中取出时, 就得到了从初态到该状态的最小代价。然而, 一个状态当前最小, 不代表从该状态到目标状态代价就最小,但是优先队列BFS会先选择这个分支, 导致搜索量增大;

所以, 我们能够想到, 设计一个函数, 计算从该状态到目标状态的代价的估计值, 仍然维护一个堆,把当前价值 + 估计价值最小作为拓展;估价函数原则是估价值 <- 实际值(不再证明。。。)

在求第k短路时, 我们把最短路作为估价, 保证估价 <= 实际, 还能顺应实际的变化趋势

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 5e5 + 100;
const int MAXM = 3e3 + 10;

template < typename T > inline void read(T &x) {
    x = 0; T ff = 1, ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') ff = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= ff;
} 

template < typename T > inline void write(T x) {
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) write(x / 10);
    putchar(x % 10 + '0');
} 

int n, m, s, t, k;
int f[MAXN], vis[MAXN];
int lin[MAXN], tot = 0, linc[MAXN], totc = 0;
struct edge {
    int y, v, next;
}a[MAXN], e[MAXN];

struct node {
    int pos, f, dis;
    bool operator < (node a) const {
        return a.f + a.dis < f + dis; 
    } 
};

inline void add(int xx, int yy, int vv) {
    a[++tot].y = yy;
    a[tot].v = vv;
    a[tot].next = lin[xx];
    lin[xx] = tot;
}

inline void addc(int xx, int yy, int vv) {
    e[++totc].y = yy;
    e[totc].v = vv;
    e[totc].next = linc[xx];
    linc[xx] = totc;
}

void SPFA() {
    queue < int > q;
    memset(f, 0x3f, sizeof(f));
    memset(vis, false, sizeof(vis));
    q.push(t); 
    f[t] = 0;
    vis[t] = true;
    while(!q.empty()) {
        int x = q.front(); q.pop();
        vis[x] = false;
        for(int i = lin[x], y; i; i = a[i].next) {
            if(f[y = a[i].y] > f[x] + a[i].v) {
                f[y] = f[x] + a[i].v;
                if(!vis[y]) {
                    vis[y] = true;
                    q.push(y);
                }
            } 
        }    
    }
} 

int astar() {
    priority_queue < node > q;
    if(f[s] == INF) return -1;
    int ts[MAXN];
    node tmp, h;
    h.pos = s; h.f = 0; h.dis = 0;
    q.push(h);
    while(!q.empty()) {
        node x = q.top(); q.pop();
        ts[x.pos]++;
        if(ts[x.pos] == k && x.pos == t) return x.dis;
        if(ts[x.pos] > k) continue;
        for(int i = linc[x.pos]; i; i = e[i].next) {
            tmp.pos = e[i].y;
            tmp.f = f[e[i].y];
            tmp.dis = x.dis + e[i].v;
            q.push(tmp);
        }
    }
    return -1;
}

int main() {
    read(n); read(m);
    for(int i = 1; i <= m; ++i) {
        int u, v, w;
        read(u); read(v); read(w);
        add(v, u, w);
        addc(u, v, w);
    }
    read(s); read(t); read(k);
    if(s == t) ++k;
    SPFA();
    write(astar());
    return 0;
}

 

posted @ 2019-03-28 12:51  海边微风起  阅读(1021)  评论(1编辑  收藏  举报