【模板】最短路模板+最小路径生成树

Dijkstra

O ( ( n + m ) l o g n ) O((n+m)logn) O((n+m)logn)
只能用于正权图

#include<bits/stdc++.h> using namespace std; const int N=1e5+5; int n,m,st,ed,dis[N]; bool vis[N]; struct edge{ int v,w; }; int read() { int f=1,x=0;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();} return x*f; } priority_queue<pair<int,int> > q; vector<edge> son[N]; void add(int x,int y,int z) { son[x].push_back((edge){y,z}); son[y].push_back((edge){x,z}); } void dijkstra() { q.push(make_pair(0,st)); dis[st]=0; while(q.size()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=0;i<son[x].size();i++) { int v=son[x][i].v,w=son[x][i].w; if(dis[x]+w<dis[v]) {//松弛操作 dis[v]=dis[x]+w; q.push(make_pair(-dis[v],v)); } } } } int main() { memset(dis,0x3f,sizeof(dis)); n=read(),m=read(),st=read(),ed=read(); for(int i=1;i<=m;i++) { int x,y,z; x=read(),y=read(),z=read(); add(x,y,z); add(y,x,z); } dijkstra(); printf("%d",dis[ed]); }

Bellman-Ford

O ( n m ) O(nm) O(nm)

如果n-1次松弛后仍然不满足三角形不等式,则说明有负环(这个环内每次都可以更新,因为一次比一次小),否则就没有负环(可以收敛)。

注意:有负环时bellman算法已经崩溃了,不能作为任何答案,所以虽然在负权下可以工作,但容易RE

判环+输出路径

#include<bits/stdc++.h> using namespace std; int n,m,st,ed,dis[505],num[505],pre[505]; struct Edge{ int u,v,w; }e[20005]; void print(int x) { if(!pre[x]) { printf("%d ",x); return; } print(pre[x]); printf("%d ",x); } bool bellman_ford(int S) { dis[S]=0; num[S]=0; pre[S]=0; for(int i=1;i<n;i++) { for(int j=1;j<=m;j++) { int u=e[j].u,v=e[j].v,w=e[j].w; if(dis[v]>dis[u]+w||(dis[v]==dis[u]+w&&num[v]>num[u]+1)||(dis[v]==dis[u]+w&&num[v]==num[u]+1&&u<pre[v])) { dis[v]=dis[u]+w; num[v]=num[u]+1; pre[v]=u; } } } for(int j=1;j<=m;j++) { int u=e[j].u,v=e[j].v,w=e[j].w; if(dis[v]>dis[u]+w) { return 0; } } return 1; } int main() { memset(dis,0x3f,sizeof(dis)); memset(num,0x3f,sizeof(num)); memset(pre,0x3f,sizeof(pre)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w); } scanf("%d%d",&st,&ed); if(bellman_ford(st)) { printf("%d\n",dis[ed]); print(ed); } else printf("No Solution"); }

Spfa

O ( k n ) O(kn) O(kn),但容易被卡(尤其是稠密图)

基于队列优化的bellman算法,负权图同样可以工作

可以用 n u m [ ] num[] num[]数组记录路径长度,只要有松弛操作就令 n u m [ v ] = n u m [ u ] + 1 num[v]=num[u]+1 num[v]=num[u]+1,若 n u m [ v ] > n num[v]>n num[v]>n则说明有负环

#include<bits/stdc++.h> using namespace std; const int maxn=100005; int n,m,dis[maxn]; bool vis[maxn]; void read(int &x) { int f=1;x=0;char c=getchar(); while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();} while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();} x*=f; } struct edge{ int v,w; edge(){} edge(int V,int W) { v=V,w=W; } }; queue<int> q; vector<edge> v[maxn]; void spfa() { q.push(1); dis[1]=0; vis[1]=1; while(q.size()) { int x=q.front();q.pop(); vis[x]=0; for(int i=0;i<v[x].size();i++) { int vi=v[x][i].v,wi=v[x][i].w; if(dis[x]+wi<dis[vi]) { dis[vi]=dis[x]+wi; if(!vis[vi]) { q.push(vi); vis[vi]=1; } } } } } int main() { memset(dis,0x3f,sizeof(dis)); read(n),read(m); for(int i=1;i<=m;i++) { int x,y,z; read(x),read(y),read(z); v[x].push_back(edge(y,z)); v[y].push_back(edge(x,z)); } spfa(); printf("%d",dis[n]); }

最小路径生成树

eg.黑暗城堡

解析:对于1到 i i i的最短路,事实上存在 j j j使 d i s [ i ] = d i s [ j ] + G ( j , i ) dis[i]=dis[j]+G(j,i) dis[i]=dis[j]+G(j,i)

这给我们启发: j j j的最短路与 i i i的最短路存在联系

如果把每一个 i i i与前继 j j j连起来,可以发现一定找得到这个 j j j,那么 i i i的最短路一定由 j j j贡献,就只需要考虑 j j j的最短路了。

题目又告诉我们这是一个树形结构,且没有负边,边是n-1,刚好n个点,所以每个点 i i i只能找一个前继点 j j j,我们只需统计num[i]表示满足条件的j的个数,然后把num[1~n]相乘即可。

至于求dis[i],可以用dijkstra预处理完成。


__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530412.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(3)  评论(0编辑  收藏  举报  
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示