堆优化的dijkstra板子*
对于单源最短路的问题,目前已知最快的解决算法就是堆优化处理过的dijkstra(条件当然是不存在负边的情况下,若存在负边的情况则要用spfa辽,但是蒻苟不会嘻嘻🤭)
dijkstra的本质是贪心,对于求目标两个位置间的最短权边和,查找从起点位置到每个点的最短距离,同时更新与该当前点相关的点的距离。
这种存图方式只需要开一个数组存储每个点引出的第一条边,然后存储每个点作为起点的每条边,这样就可以做到不重不漏。
在链式前向星存图中,我们需要定义一个结构体:
1 2 3 4 5
| struct EDGE { int next; int to; }edge[1000000];
|
和一个数组
和一个变量:
你会发现竟然没存起点!!其实起点是用headhead存的
举例:

如图:这样的一个有向图,输入是:
逐步分析:
1.输入1 2,代表1连向2。
1 2 3
| cnt++;//作为结构体下标,没有意义 head[1]=cnt;//结点1的第一个儿子存在了edge[cnt]里面 edge[cnt].to=2;结点1的儿子是2
|
此时: cnt=1

2.输入1 3,代表1连向3。
1 2 3 4 5 6 7 8 9 10
| cnt++; head[1]=cnt; edge[cnt].to=3;结点1的儿子是3 //这时,3成为了结点1的儿子,不过2被挤了下去... //所以要引入结构体中next元素,记录:3还有个兄弟(next)是2 //所以代码要换成: cnt++; edge[cnt].to=3;//结点1连向3 edge[cnt].next=head[1];//3的兄弟是2 head[1]=cnt;//更新head
|
此时: cnt=2

3.输入1 4,代表1连向4。
此时cnt=3

4.输入2 3,代表2连向3。
此时: cnt=4

可以理解的是,next存的是当前结点连接的最近的兄弟结点的下标,如1->2,1->3,1->4,则4的next是3,3的next是2。
而head 存的是当前结点指向的最远的结点的下标,1->2,1->3,则head [1] =3,
对于带权值的问题,在结构体中加入一个元素记录权值即可
代码:(带权值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include<iostream> using namespace std; struct edge { int next; int to; int wei; }edge[MAXM]; int head[MAXN]; int cnt=0; void addedge(int u,int v,int w) { edge[++cnt].next=head[u]; edge[cnt].to=v; edge[cnt].w=w; head[u]=cnt; } int main() { int n; for(int i=1;i<=n;i++) { int a,b,wei; addedge(a,b,wei); } }
|
边的遍历
在遍历以x为起点的所有边时,只需要这样就行
1 2
| for(int i=head[x];i!=0;i=edge[i].next)
|
这个循环的结束条件是i等于0,因为最后一条边,也就是存边时第一条边,在把head值存进next时,head还没有更新过,也就是0。所以当next返回0时,就说明这些边遍历完毕了。
代码
堆优化
在寻找最短值的时候,用优先队列priority_queue<pair<int,int>>来存储,其中的pair中记录的分别是每条边的权值和终点结点。
优化完成后的总复杂度为O(mlogn)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| #include <bits/stdc++.h> #define rep(i,n,m) for(int i=n;i<=m;++i) #define per(i,n,m) for(int i=n;i>=m;--i) #define INF 0x3f3f3f3f using namespace std; typedef long long ll; const int N=250010; int head[N],ne[N],to[N],w[N],dist[N];
bool st[N]; int n,m,s,cnt=0; void addw(int a,int b,int c){ w[++cnt]=c; to[cnt]=b; ne[cnt]=head[a]; head[a]=cnt; } void Dijkstra() { priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> heap; heap.push({0,s});dist[s]=0; while(!heap.empty()){ pair<int,int> temp=heap.top(); heap.pop(); int x=temp.first,y=temp.second; if(st[y])continue; st[y]=true; for(int i=head[y];i!=0;i=ne[i]){ int t=to[i]; if(dist[t]>x+w[i]){ dist[t]=x+w[i]; heap.push({dist[t],t}); } } } } int main() { cin>>n>>m; memset(dist,INF,sizeof(dist)); for(int i=1;i<=m;++i){ int a,b,c;cin>>a>>b>>c; addw(a,b,c); } Dijkstra(); rep(i,1,n)cout<<dist[i]<<' '; return 0; }
|
带负权边问题的spfa算法之后再考虑学不学🙄🙄🙄🙄