前言
最近几天,学习完了一些最短路径算法,由于学习路程艰难曲折,所以写下了这篇博客来总结一下
———————————————————————————————————————————————————————————
正文
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++
不喜勿喷