/*
*/
把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【模板】dijkstra与floyd

(我永远喜欢floyd)

温馨提示:与SPFA一起食用效果更佳

传送门:https://www.cnblogs.com/Daz-Os0619/p/11388157.html

Floyd

大概思路:

对于某两个点来说,查找他们之间是否连通,若连通,是记录他们间的最短路。

这里的最短路有两个方向:一个是直接到,一个是通过另外一个点到。(十分单纯的最短路了)

不需要多讲上代码!

void floyd() {
    for (int k = 1; k <= n; ++k)
        for (int i = 1; i <= n; ++i)
            for (int j = 1; j <= n; ++j)
                dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}

Dijkstra

大概思路:

(跟SPFA差不多??)

有一个集合P,一开始只有源点S在P中,每次更新都把集合之外路径值最小的一个放入集合中,更新与它相连的点,重复前面的操作直到集合外没有元素。

特殊要求:所有边权要是正的

这时的时间复杂度为O(n*n) (比较危险)

可以采用堆来优化 时间复杂度O(n*logn)

(蒟蒻发言:怎么用堆优化?)

 一颗完全二叉树。

这里用到的小根堆是最小值作为堆顶元素,大根堆反之。

大概长下面这样:

 

利用小根堆优化Dijkstra的理由与用SLF优化SPFA的理由相同,核心思想也相近,但是写法差异很大。

核心思想:因为每一次都会从队首开始遍历,当队首是最小值时,被更新的所以节点的值也会是最小值,这样可以节省很大一部分时间。

(这里就体现优先队列的好处)

 下面是核心代码(可以先看注释,后面有图解):

struct edges {
    int next, to, v;
    edges() {}
    edges(int _n, int _t, int _v) : next(_n), to(_t), v(_v) {}//next表示与这条边同起点的上一条边 
} e[];

struct heap_node {
    int v, to;
    heap_node() {}
    heap_node(int _v, int _to) : v(_v), to(_to) {}
};
inline bool operator < (const heap_node &a, const heap_node &b) {
    return a.v > b.v; //这里实现的是小根堆,如果要大根堆,把 > 改成 < 即可
}

priority_queue <heap_node> h;

inline void add_to_heap(const int p) {
    for (int x = first[p]; x; x = e[x].next)//first表示以first为头的最后一条边 
        if (dis[e[x].to] == -1)//如果下一条边还没在堆里 
            h.push(heap_node(e[x].v + dis[p], e[x].to));//加上前一个点的最短路值(跟新),放入堆 
}

void Dijkstra(int S) {
    memset(dis, -1, sizeof(dis));//清空(便于将数据放入堆里) 
    while (!h.empty()) h.pop();//清空堆 
    dis[S] = 0, add_to_heap(S);//现将所有与源点相连的点放入堆中 
    int p;
    while (!h.empty()) { //堆空时所有的点都更新完毕(与SPFA相反) 
        if (dis[h.top().to] != -1)//如果与堆顶元素相连的点已经有了最短路值 
         {
            h.pop();//弹掉堆顶 (此时堆顶不会被更新了) 
            continue;//一直找到可以被更新的点 
        }
        p = h.top().to;//找到与堆顶相连且不在堆里的点 
        dis[p] = h.top().v;//更新最短路值(只是堆顶的) 
        h.pop();//弹掉 
        add_to_heap(p);//再更新与它相连的点,并将与它相连的点放入堆 
    }
}

好长的注释啊。。

再画个图解释一下

(这里的样例和SPFA的样例一样)

后面就这样一直循环直到队列为空。为空时所有最短路更新完毕。

完整代码:

#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
struct edge
{
    int next,to,v;
    edge(){}
    edge(int a,int b,int c)
    {
        next=a;
        to=b;
        v=c;
    }
}E[10001];
int first[10001],n,dis[100001];
int S;
struct heap
{
    int to,v;
    heap(){}
    heap(int a,int b)
    {
        to=a;
        v=b;
    }
};
inline bool operator< (const heap &a,const heap &b)
{
    return    a.v>b.v;
}
priority_queue<heap> h;

void add_to_heap(int p)
{
    for(int i=first[p];i;i=E[i].next)
    {
        if(dis[E[i].to]==-1)
        h.push(heap(E[i].to,dis[p]+E[i].v));
    }
}
int tot;
void add_edges(int a,int b,int c)
{
    E[++tot]=edge(first[a],b,c);
    first[a]=tot;
}
void dijkstra(int S)
{
    while(!h.empty())
    h.pop();
    memset(dis,-1,sizeof(dis));
    dis[S]=0;
    add_to_heap(S);
    int p;
    while(!h.empty())
    {
        if(dis[h.top().to]!=-1)
        {
            h.pop();
            continue;
        }
        p=h.top().to;
        dis[p]=h.top().v;
        h.pop();
        add_to_heap(p);
    }
}
int m;
int main()
{
    cin>>n>>m>>S;
    int A,B,C;
    for(int i=1;i<=m;i++)
    {
        cin>>A>>B>>C;
        add_edges(A,B,C);
    }
    dijkstra(S);
    for(int i=1;i<=n;i++)
    cout<<dis[i]<<endl;
    return 0;
}

 

结束!

今天也想放烟花!

posted @ 2019-09-10 23:36  Kyoko_Yosa  阅读(303)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end