题目链接
参考博客
这个大佬把好多单源最短路径问题的解法代码都写了一遍,tql!!!
大佬
优先队列优化Dijkstra算法
学了一晚上终于看明白了,因为大佬觉得很简单,所以注释并不细致,导致我这个小白理解了好久。
首先说明,要是不知道Dijkstra算法,先看看“啊哈C”里对于此算法的讲解再看此优化题解。
进入正题:
优先队列,通过堆实现,即二叉树,只要知道它是个队列,队首总是放着最大或者最小的数(默认是最大的数,可以手动调整为最小数在队首)。
优化正是利用了优先队列的这个特性。
还是通过代码注释来讲解。
先提醒一下,代码中嵌套使用了许多STL容器,所以理解起来比较困难,但不要放弃,多读几遍,一定会懂!我就是这样的!更何况我会详细的讲解,你还有什么理由放弃!
AC代码(模板)
#include<bits/stdc++.h> #define pb push_back//!!! using namespace std; const int INF=0x3f3f3f3f; const int N=1e5+100; int n,m,s; struct edge{ int to,cost; };//to代表点的编号,cost代表某个点到to点的权值。配合下面的vector G理解一下。 typedef pair<int,int> P;//下面所用到的P,P.second代表一个点的编号,P.first表示起点s到P.second的距离。(下面会讲为什么first代表距离,second代表编号) //对了,还得补充一下,pair可以比较大小,他们比较大小的方式是先比较first,若同,再比second。 vector<edge> G[N]; /* G[v][i].to表示与v点相连的第i个点的编号,G[v][i].cost表示v点与v点相连的第i个点之间的距离,这个距离仅仅是输入时候的那个距离,在整个程序运行的过程中是不变的,也就是说我们只是用这里面存储的值,却不更新存储的值。 */ int d[N];//存储起点到其他各点的距离 void create(){ cin>>n>>m>>s; for(int i=1;i<=m;i++){ int from,to,cost; cin>>from>>to>>cost; edge tmp; tmp.to=to;tmp.cost=cost;//把tmp结构体赋值好,放在G容器中 G[from].pb(tmp);//之所以struct edge中不定义from变量,正是因为G的索引已经表明了tmp.to的起点(此起点为某条边的起点,并非起点s),没必要再定义from变量存起点了。 } fill(d+1,d+n+1,INF); /* find函数,在c++中用于全部赋值,有点类似于memset,但是显然比memset好用。 前俩参数和sort中的前俩参数一个意义,第三个参数是被赋予的值。 这里,我们先让全部点之间的距离都赋值为INF,保证通过比较大小更新距离 */ }//这样看来,这整个过程无非就是输入数据并创建了个G,用来存储边与边权,同时对d数组进行了初始化。其实,G才是重点。 void Dijkstra(){ priority_queue<P, vector<P>, greater<P> > que; /* 懵了没?我当时懵了。 尖括号内三个东西,其中,第一个P表示que中存储的类型;第二个vector<int>填写的是来承载底层数据结构堆(heap)的容器,第三个若为less<int>表示数字大的优先级越大,若为greater<int>表示数字小优先级越大。 按照greater存放,实现了把小的数存在队首,可以通过top直接取出最小的数,不用再像老版中Dijkstra中循环找最小值更新了,我们直接取出已经排号的最小数就行了,方便的很! 注意,vector,greater里的类型都是pair类型! */ que.push(P(0,s)); /* P(0,s)等价于pair<int,int> p(0,s),就是把一个pair初始化了,只不过P(0,s)这样初始化并没有明确地把这俩数赋到某个pair类型的变量上,而是直接入栈了。 语句含义:把s点和s点到s点的距离0,打包入栈。(如果这里忘记了P的含义,往上面翻翻再看看) */ d[s]=0;//起点s到起点s的距离设置为0; while(!que.empty()){ P p=que.top();que.pop(); int v=p.second; if(d[v]!=p.first) continue;//就是这个语句,大佬的注释都极其模糊,甚至不注释,我想了好久这里,想明白这里之后直接豁然开朗,整个代码都明了了。 /*下面的几句话实现的是刷新某点到起点s的最短距离,但是刷新前的点的信息依旧在队列里,我们得把它们弹出,判断哪些是失效的信息的标准就是比较 通过已刷新的数组d去访问此点到起点s的距离 与 通过队列里的first去访问此点到s点的距离,相等说明没刷新,不相等说明刷新了,你已经没用了,可以弹出了,因为我们已经保存了最新信息。 再说简单点,刷新之后的某点对应的d值应该是要等于p.first的,不等于的话,说明d更新了,p.first没更新,信息失效,弹出。 */ for(int i=0;i<G[v].size();i++){ edge e=G[v][i];//暂存一下v连着点的信息 if(d[e.to]>d[v]+e.cost){//e.to是与v相连点的编号,e.cost是二者的边权 d[e.to]=d[v]+e.cost; que.push(P(d[e.to],e.to));//这才是刷新之后的有效信息,压入了,所以上面代码就可以把之前压入栈中的信息弹出了。 } } } } void output(){ for(int i=1;i<n;i++) cout<<d[i]<<' '; cout<<d[n];//最后一个不输出空格而已 }//简单至极,输出而已 void solve(){ create();//创建过程 Dijkstra();//核心代码 output();//输出 } int main(){ solve(); }