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

DIJKSTRA

注意Dijkstra不能处理带负权的图,若带负权请使用spfa外加判断各点入队的次数。

 

Dijkstra的过程跟之前我写过的Prim最小生成树的算法很类似,貌似没差别。当边不是很密集的时候bfs就可以搞定,当边比较多时Dijkstra比较稳定。所以对于边密集类型用常用邻接矩阵来存图,vis【】数组来判定到达某点的最短路径是否已经确定(有点像Prim中判断点是否进入树中),起点必然是确定的,接着桶排思想枚举剩下的点找到到已经确定的点的区域(类似Prim中的树)距离最短的点,标记进入确定最短的点集,由于新点的加入,再枚举一次剩下的点,更新到这个新生成的点域的最小距离。总上为一个大循环(每次大循环结束确定一个新的点,故循环N-1次),两个内嵌的小循环,跟Prim一毛一样~。所以其实最小生成树就是一个图单源最短路径的一个体现(个人见解,若有不当欢迎指正)。复杂度为O(n^2)还是比较暴力的,一般使用堆优化和链式向前星的版本。我们先来看看最朴素的版本

#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(N)降为O(logN),之后的更新操作由于是邻接表只要遍历临边,故复杂度上限为O(ElogN)。下面这个来自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;
}

 

转载于:https://www.cnblogs.com/GorgeousBankarian/p/11147296.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值