关于最短路径算法的一些小结(Dijkstra,floyd,spfa)

前言
最近几天,学习完了一些最短路径算法,由于学习路程艰难曲折,所以写下了这篇博客来总结一下
———————————————————————————————————————————————————————————

正文

floyd
首先,提到最短路径算法,最好写的肯定是Floyd算法,如果图的点数没有超过300的话,一般的题目还是能卡过去的
floyd算法,主要是通过枚举中转点(其实就是每个点),对全图都实现松弛操作,其实本质还是dp的思路,由于其较为简单,此处便不再进行的过多的讲解
附上代码:

void floyd()
{
    for (int k=1; k<=n; k++)
        for (int i=1; i<=n; i++)
            for (int j=1; j<=n; j++)
                dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j]);//dist[i][j]数组表示全图中i,j两点的距离;松弛操作
}

floyd其实还可以寻找最小环,只需将dist数组初始化时改为无穷大,dist[i][i]设为无穷大,当跑完floyd时,
扫一遍,min=min(dist[i][i],min);即可

spfa(Shortest Path Faster Algorithm)
关于spfa———它死了
如果经(shen)常(shou)做(qi)题(hai)的同学会知道,如果是国内的比赛,
80%是会卡掉spfa的(毕竟不卡不舒服码),如NOI2018Day1T1归程 ,
甚至还有hdu4889专门造数据卡掉spfa,所以对于spfa还是慎用。
spfa算法其实是Bell-Ford算法的队列优化,如果不是特意造数据卡的话,还是挺快的
对于spfa其算法本质是:
设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
下面附上代码:

void spfa()
{
     queue<int>q;//队列
     while(!q.empty())
     q.pop();
     q.push(s);
     vi[s]=1;//已经被访问过,标记一下
     int sp,t;
     while(!q.empty())
     {
               sp=q.front();
               q.pop();
               vi[sp]=0;
               for(int i=head[sp];i;i=e[i].next)
               {
                       if(dis[e[i].t]>dis[sp]+e[i].w)//类似于Floyd的松弛操作
                       {
                                  dis[e[i].t]=dis[sp]+e[i].w;
                                  if(vi[e[i].t]==0)
                                  {
                                            q.push(e[i].t);//入队,用此节点作为中转点继续松弛
                                            vi[e[i].t]=1;
                                  }
                       }
               }
     }
}

段凡丁论文中的复杂度证明 (O(kE),k 是小常数)是错误的,所以该算法的最坏复杂度其实是 O(VE)。
但是Spfa并不是一无所用,对于存在负边权的图(非三角网格图)还是挺快的;

Dijkstra
可以这么来说,Dijktra应该是目前最稳的算法,对于不用堆优化的时间复杂度为O(n^2),但是用Dijkstra不用堆优化,
就失去存在的意义了,Dijktra算法,其实本质上还是一个BFS,他使用BFS解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。
Dijktra算法的大致思路:
把源点放入优先队列(堆)中,然后再把所有其连出去的边所到的终点,利用这个终点再对与他间接相连的点进行松弛,直到优先队列为空为止,再把所有进队的点都标为已访问过,确保不多次进队(spfa被卡是因为其将很多点多次入队),但对于存在负边权的图,
Dijktra就没有用武之地了,
下面附上代码:

struct Node
{
    int id, d; 
    bool operator < (const Node &b)const//重载运算符
    {
        return d > b.d;
    }
};
void dijkstra(int s)
{
    for (int i=1; i<=n; i++)
        dis[i]=inf,vis[i]=0;

    dis[s] = 0;
    priority_queue <Node> q;
    q.push((Node){s, 0});
    while (!q.empty())
    {
        Node now=q.top();
        q.pop();
        if (vis[now.id])
            continue;
        vis[now.id]=1;
        for (int i=0;i<v[now.id].size();i++)
        {
            //now.id---c[now.id][i]--->v[now.id][i]
            if (dis[now.id]+c[now.id][i]<dis[v[now.id][i]])
            {
                dis[v[now.id][i]]=dis[now.id]+c[now.id][i];
                q.push((Node){v[now.id][i],dis[v[now.id][i]]});
            }
        }
    }
}

好了,单源最短路径就先谈到这里,希望大家多多做题以达到能熟练(7分钟以内)敲完,见题就秒的熟练程度,
祝大家 noip2018RP++,2019NOI++

不喜勿喷

猜你喜欢

转载自blog.csdn.net/mzj_best/article/details/82598412
今日推荐