图论--单源最短路之--Dijkstra

DIJKSTRA

注意Dijkstra不能处理带负权的图,若带负权请使用spfa外加判断各点入队的次数。以下的描述都是基于图中的边无负权。

 Dijkstra简述

  dij是基于贪心的一种求解单源最短路径的算法,效率非常高。贪心算法的求解是来源于最优子结构的性质,从局部最优逐渐构造到全局最优。

  • 最优子结构性质:最短路径的子路径仍然是最短路径。
  • 贪心选择性质:最短子路径长度递增,逐次生成最短路径。

算法步骤

vis数组用于标记顶点的最短路径是否确定,dis标记顶点到源点的最短路径长度。

  1. 初始初始时dis[源点]为0,其他初始化为INFINIRTE(无穷大)。
  2. 遍历图中的所有点,找到dis最小的且未确定最短路的点u,此时u的最短路径就是dis了,标记u的最短路径已经确定。 *** 最优子结构 *** 
  3. 更新u的所有邻接顶点到源点的最短路径长度dis。 *** 最优子结构的拓展构造;最短路径或是直接从源点到该点,或是经过其他顶点到该点。***
  4. 重复2、3直至所有顶点都被确定,或无法再继续到达其他顶点,结束算法

对于这种最朴素的dijkstra,时间复杂度为O(V2 + E)。

#define Inf 0x3f3f3f3f
const int maxn = 1e3 + 2;

int dis[maxn], table[maxn][maxn], N;
bool vis[maxn];    //记录是否已经找到达该点的最小值

void dijkstra()
{
    for(int i=1; i<=N; ++i)
        dis[i] = table[i][s];
    dis[1] = 0;
    memset(vis, false, sizeof(vis));
    vis[s] = true;

    for(int i=1; i<=N; ++i){
        int index = -1, mindis = Inf;
        for(int j=1; j <= N; ++j){
            if(!vis[j] && dis[j] < mindis){ // 遍历所有点找到到确定点集距离最小的点
                mindis = dis[j];
                index = j;
            }
        }
        if(index = -1) break;    //最短路不存在
        vis[index] = true; // index点到源点的最短路确定!
        for(int j=1; j<=N; ++j){ // 更新剩余点的最短路
            if(dis[j] > dis[index] + table[index][j] && !vis[j])
                dis[j] = dis[index] + table[index][j];
        }
    }
    return;
}

 

优化

下面对朴素的算法进行优化。1)邻接表存图:减少不必要的点的遍历。2)利用priotity_queue:堆优化找点的过程,这样子找点的复杂度就由原本的O(V)降为O(logV),故复杂度上限为O((E+V)logV)。对于这个复杂度的问题,我觉得有必要好好说说:

总的点数V,总的边数E。

  • O(logV):用于在堆中找出dis最小的顶点。最优时是根节点,然而许多时候并不是,那么根节点出堆之后堆的维护特性就需要O(logV)的复杂度。所以对于V个顶点的图,最坏情况查找到顶点复杂度VO(logV)。
  • O(logV):确定完一个dis最小的顶点u之后,更新u所有邻接点的dis,更新的邻接点v入堆,堆的插入操作就是O(logV)。u的临界点的数量最多就是边的数量E,所以更新dis部分的最坏复杂度EO(logV)。
  • 所以总的时间复杂度就是:VO(logV)+EO(logV) = (V+E)log(V)

下面这个来自poj的一道题,这里的dijkstra用了priority_queue优化,其实就是将大循环里的第一个循环找最小的节点的操作让priority_queue自行完成。题目为Sightseeing。这里的dijkstra不仅堆优化,而且同时查找了最短路和次短路,所以原本的数组都增加了一维。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
#define ll long long 
#define Inf    2147483647
const int maxe = 50000 + 5 maxv = 50000 + 5;
struct Edge{
    int to, w;    int next;
    Edge(int a=0, int b=0, int c=0):to(a), w(b), next(c){}
}e[maxe * 2];
int head[maxv], cnte, pw[amxv];
struct Node{
    int u; ll dis;
    Node(int a=0, ll b=0):u(a), dis(b){}
    bool operator < (const Node& b) const{
        return this->dis > b.dis;
    }
};
bool vis[maxv];
ll ans, dis[maxv];
int nv, ne, a, b, w, st;

inline void add_edge(const int& from, const int& to, const int& w){
    ++cnte;
    e[cnte].to = to, e[cnte].w = w;
    e[cnte].next = head[from];
    head[from] = cnte;
    return;
}
void dijkstra(){
    memset(vis, false, sizeof(vis));
    for(int i=1; i<=nv; ++i) dis[i] = Inf;    //注意这里的Inf不是0x3f3f3f3f只能用循环赋值
    dis[st] = 0;
    priority_queue<Node> pq;    while(pq.size())    pq.pop();
    pq.push(Node(st, 0));
    while(pq.size()){    
        Node cur = pq.top();
        pq.pop();
        if(vis[cur.u]) continue;
        vis[cur.u] = true;
        for(int i = head[cur.u]; i; i = e[i].next)
        {
            if(dis[e[i].to] > dis[cur.u] + e[i].w){
                dis[e[i].to] = dis[cur.u] + e[i].w;
                pq.push(Node(e[i].to, dis[e[i].to]));
                //无需再判断是否已经确定,直接入队即可,因为每次弹出的值都是经过筛选的最小值
                //且访问过的不会再
            }
            else continue;
        }
    }
    return;
}
model

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int N = 1e3 + 5, M = 1e4 + 5, Inf = 0x3f3f3f3f;
struct Edge{
    int to, w;    int next;
}e[M];
int head[N], cnte;
int t, n, m, u, v, w, st, en;
int dis[N][2], vis[N][2], cnt[N][2];
struct Node{
    int index, dis, type;
    Node(int a=0, int b=0, int c=0):index(a), dis(b), type(c){}
    bool operator < (const Node& b) const{
        return this->dis > b.dis;
    }
};//看到这各重载应该就懂要干嘛了吧,对,用priority_queue优化dijkstra!

void dijkstra(){
    priority_queue<Node> pq;
    memset(vis, 0, sizeof(vis));
    memset(cnt, 0, sizeof(cnt));
    for(int i=1; i<=n; ++i) 
        dis[i][0] = dis[i][1] = Inf;
    dis[st][0] = 0;    cnt[st][0] = 1;//最短路确定存在
    // pq.push({st, 0, 0});
    pq.push(Node(st, 0, 0));

    while(pq.size()){ 
        Node cur = pq.top();    pq.pop();
        if(vis[cur.index][cur.type]) continue;
        vis[cur.index][cur.type] = true;

        for(int i = head[cur.index]; i; i = e[i].next)
        {    
            int nindex = e[i].to, w = e[i].w;
            if(dis[nindex][0] > dis[cur.index][cur.type] + w){
                dis[nindex][1] = dis[nindex][0];
                cnt[nindex][1] = cnt[nindex][0];

                dis[nindex][0] = dis[cur.index][cur.type] + w;
                cnt[nindex][0] = cnt[cur.index][cur.type];

                // pq.push({nindex, dis[nindex][0], 0});
                // pq.push({nindex, dis[nindex][1], 1});
                pq.push(Node(nindex, dis[nindex][0], 0));
                pq.push(Node(nindex, dis[nindex][1], 1));
            }
            else{
                if(dis[nindex][0] == dis[cur.index][cur.type] + w)
                    cnt[nindex][0] += cnt[cur.index][cur.type];
                else{
                    if(dis[nindex][1] > dis[cur.index][cur.type] + w){
                        dis[nindex][1] = dis[cur.index][cur.type] + w;
                        cnt[nindex][1] = cnt[cur.index][cur.type];

                        // pq.push({nindex, dis[nindex][1], 1});
                        pq.push(Node(nindex, dis[nindex][1], 1));
                    }
                    else{
                        if(dis[nindex][1] == dis[cur.index][cur.type] + w)
                            cnt[nindex][1] += cnt[cur.index][cur.type];
                    }
                }
            }
        }
    }
    return;
}
inline void add_edge(const int& from, const int& to, const int& w){
    ++cnte;
    e[cnte].to = to;    e[cnte].w = w;
    e[cnte].next = head[from];
    head[from] = cnte;
    return;
}
inline int read(){
    char ch = getchar();
    int ans = 0;
    while(ch<'0' || ch>'9') ch = getchar();
    while(ch >= '0' && ch <= '9'){
        ans = (ans << 3) + (ans << 1) + ch - '0';
        ch = getchar();
    }return ans;
}
int main()
{
    t = read();
    while(t--){
        memset(head, 0, sizeof(head));    cnte = 0;

        n = read(), m = read();
        for(int i = 1; i <= m; ++i){
            u = read(), v = read(), w = read();
            add_edge(u, v, w);
        }
        st = read(); en = read();
        dijkstra();
        if(dis[en][1] == dis[en][0] + 1)
            cnt[en][0] += cnt[en][1];
        printf("%d\n", cnt[en][0]);
    }
    return 0;
}
posted @ 2019-07-07 19:20  Bankarian  阅读(276)  评论(0编辑  收藏  举报