D92【模板】最短路径树 Dijkstra 算法 CF545E Paths and Trees
D92【模板】最短路径树 Dijkstra 算法 CF545E Paths and Trees_哔哩哔哩_bilibili
最短路径树(ShortestPathTree, SPT)是连通图的一个生成树,从根到任意点的路径都为原图中根到任意点的最短路径
最小生成树(Minimum Spanning Tree, MST)是连通图的一个生成树,包含所有的顶点且边权之和最小
最短路径树(左图)的边权和 $\ge$ 最小生成树(右图)的边权和

以某点为根的最短路径树可能有多颗,合起来就构成最短路径图,最短路径图可以看做以根为起点的 DAG 图
如何求最短路径树?
最短路径树是从根节点到任一点的路径都为最短路,可以用单源最短路径算法 Dijkstra
从根节点开始扩展,每次松弛操作,更新最短路 d 数组,同时使用 pre 数组来记录点 v 的前驱边
要保证生成树上所有的边权和最小,还要考虑一种情况,如果松弛前的结果与松弛后的结果相等(d[v]==d[u]+w),并且当前这条前驱边比以前的前驱边更小(w<w[pre[v]] ),那么更新当前前驱边的 pre[v]=i
图中节点 3 先被前驱边 3 更新,后被前驱边 2 更新。后一次被更新一定更小,所以松弛条件也可以改为:d[v]>=d[u]+w


因为用前向星存边,双向边的编号是挨着的,所以输出边的编号 $\frac{pre[i]+1}{2}$
相关板子:
D02【模板】最短路 Dijkstra 算法 P4779 单源最短路径 - 董晓 - 博客园
// 最短路树 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define ll long long #define pli pair<ll,int> using namespace std; const int N=3e5+5; int h[N],to[N<<1],ne[N<<1],idx; ll ww[N<<1]; void add(int u,int v,ll w){ to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx; } int n,m,s; int pre[N]; bool vis[N]; ll d[N]; void dijkstra(int s){ memset(d,0x3f,sizeof(d)); d[s]=0; priority_queue <pli,vector<pli>,greater<pli> > q; q.push({0,s}); while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u])continue; vis[u]=1; for(int i=h[u];i;i=ne[i]){ int v=to[i],w=ww[i]; if(d[v]>d[u]+w){ d[v]=d[u]+w; pre[v]=i; //保存前驱边 q.push({d[v],v}); } if(d[v]==d[u]+w && w<ww[pre[i]]) pre[v]=i; //保存前驱边 } } } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } scanf("%d",&s); dijkstra(s); ll sum=0; for(int i=1;i<=n;i++)if(i!=s)sum+=ww[pre[i]]; //计算最短路树的边权和 printf("%lld\n",sum); for(int i=1;i<=n;i++)if(i!=s)printf("%d ",(pre[i]+1)/2); }
// 最短路树 Dijkstra 算法 O(MlogN) #include<bits/stdc++.h> #define ll long long #define pli pair<ll,int> using namespace std; const int N=3e5+5; int h[N],to[N<<1],ne[N<<1],idx; ll ww[N<<1]; void add(int u,int v,ll w){ to[++idx]=v;ww[idx]=w;ne[idx]=h[u];h[u]=idx; } int n,m,s; int pre[N]; bool vis[N]; ll d[N]; void dijkstra(int s){ memset(d,0x3f,sizeof(d)); d[s]=0; priority_queue <pli,vector<pli>,greater<pli> > q; q.push({0,s}); while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u])continue; vis[u]=1; for(int i=h[u];i;i=ne[i]){ int v=to[i],w=ww[i]; if(d[v]>=d[u]+w){ d[v]=d[u]+w; pre[v]=i; //保存前驱边 q.push({d[v],v}); } } } } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y,z;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); add(x,y,z);add(y,x,z); } scanf("%d",&s); dijkstra(s); ll sum=0; for(int i=1;i<=n;i++)if(i!=s)sum+=ww[pre[i]]; //计算最短路树的边权和 printf("%lld\n",sum); for(int i=1;i<=n;i++)if(i!=s)printf("%d ",(pre[i]+1)/2); }
浙公网安备 33010602011771号