算法的原理以及邻接矩阵实现见先前写的单源最短路径。
相较于邻接矩阵,邻接矩阵在进行松弛操作时需要遍历每一条边,而基于链式前向星的实现仅需执行出度的个数次,大大节省了循环次数。
Dijkstra
下方算法中,dist
记录了每个节点当前的最短路径,初始值设定为无穷大;flag
记录了先前每一轮循环中未确定答案的点中路径最短的点,即已经获得答案的点;p
记录了节点的直接前驱。
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
| flag[1]=true; dist[1]=0; for(int i=head[1]; i!=-1; i=edge[i].next) { dist[edge[i].to]=edge[i].weight; p[edge[i].to]=1; }
for(int i=1;i<=n-1;i++) { int temp=0x7f7f7f, t=1; for(int j=1;j<=n;j++) { if(flag[j]==false && dist[j]<temp) { temp=dist[j]; t=j; } } if(t==1) break; flag[t]=true; for(int k=head[t]; k!=-1; k=edge[k].next) { int j=edge[k].to; if(flag[j]==false && dist[j]>dist[t]+edge[k].weight ) { dist[j]=dist[t]+edge[k].weight; p[j]=t; } } }
|
SPFA
此处实现中的pd
用于判断是否存在负环,其余部分为标准SPFA。
判断负环的原理:判断入队次数是否>=n,如果是则说明有负环。
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
| int dis[maxn], cnt[maxn]; bool inq[maxn], pd=false; memset(dis, 0x3f3f3f3f, sizeof(dis)); memset(cnt, 0, sizeof(cnt)); memset(inq, false, sizeof(inq)); int n,m; cin>>n>>m; for(int i=1,u,v,w;i<=m;i++) { cin>>u>>v>>w; if(w>=0) { addEdge(u,v,w); addEdge(v,u,w); } else { addEdge(u,v,w); } } queue<int> q; q.push(1); dis[1]=0; inq[1]=true; while(!q.empty()) { int cnthead=q.front(); q.pop(); inq[cnthead]=false; for(int i=head[cnthead];i!=-1;i=edge[i].next) { int to=edge[i].to, weight=edge[i].weight; if(dis[to]>dis[cnthead]+weight) { dis[to]=dis[cnthead]+weight; if(!inq[to]) { if(++cnt[to]>=n) { pd=true; break; } inq[to]=true; q.push(to); } } } if(pd==true) break; } if(pd==true) cout<<"YES"<<endl; else cout<<dis[n]<<endl;
|