【学习笔记】 Johnson 全源最短路
介绍
由于容易被卡,实际上我们在 的算法只有堆优化的
由于先天问题,无法处理在负权图上的问题
所以“全源最短路”算法就应运而生了
算法流程
我们针对无法处理负权图进行优化
我们考虑如何把每条边的权值转化成正数
这里引入“势能”的概念
势能需要一个起始点:建立一个虚拟源点,向每一个点连一条权值为的有向边
跑一遍,记录每个点到虚拟源点的最短路,记为 ,因为是
(这里问显然的同学,请注意这可能是一个负权图)
然后我们把每一条边的边权加上两端势能: res[e[num].from]-res[e[num].to];
然后我们跑次,最后将势能减去,即 ans[from][to]-=res[from]-res[to];
复杂度
const int N=4e3+10;
struct node{int to,dis,nxt;}e[N<<3];
int head[N],n,m,cnt,app[N],res[N],ans,tmp[N]; bool vis[N];
inline void add(int u,int v,int w){
e[++cnt].dis=w; e[cnt].nxt=head[u]; e[cnt].to=v;
return head[u]=cnt,void();
}
inline bool spfa(int s){
queue<int> q; memset(res,0x3f,sizeof(res));
q.push(s); vis[s]=1; res[s]=0;
while(!q.empty()){
int fr=q.front(); q.pop(); vis[fr]=0;
for(int i=head[fr];i;i=e[i].nxt){
int t=e[i].to,dist=e[i].dis+res[fr];
if(res[t]>dist){
res[t]=dist;
if(!vis[t]){
// Negative Circle
if(++app[t]>=n) return 0;
vis[t]=1; q.push(t);
}
}
}
}
return 1;
}
#define mp make_pair
inline void dij(int s){
priority_queue<pair<int,int> > q;
q.push(mp(0,s)); memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i) tmp[i]=1e9; tmp[s]=0; ans=0;
while(!q.empty()){
int fr=q.top().second; q.pop();
if(vis[fr]) continue; vis[fr]=1;
for(int i=head[fr];i;i=e[i].nxt){
int t=e[i].to,dist=e[i].dis+tmp[fr];
if(tmp[t]>dist){
tmp[t]=dist;
if(!vis[t]) q.push(mp(-dist,t));
}
}
}
for(int i=1;i<=n;++i){
if(tmp[i]==1e9) ans+=tmp[i]*i;
else ans+=i*(tmp[i]+res[i]-res[s]);
}
return printf("%lld\n",ans),void();
}
signed main(){
n=read(); m=read(); for(int i=1,u,v,w;i<=m;++i) u=read(),v=read(),w=read(),add(u,v,w);
for(int i=1;i<=n;++i) add(n+1,i,0); if(!spfa(n+1)) return puts("-1"),0;
for(int i=1;i<=n;++i) for(int j=head[i];j;j=e[j].nxt) e[j].dis+=res[i]-res[e[j].to];
for(int i=1;i<=n;++i) dij(i);
return 0;
}
应用前景
这个破玩意学它有什么用呢?
求解最小费用最大流就是不断求解最短路,然后通过最短路增广的过程
由于走反向边费用要取负,无法使用 增广,只能通过
而有了 算法以后,就可以先做一轮 ,
然后不断通过 增广,运行更稳定,效率更高
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律