20250812 全源最短路总结

部署运行你感兴趣的模型镜像

引言

全源最短路,说白了就是从任意一个点出发到任意一个点的最短路,也就是所有点对之间的最短路。以下是几种可能用得到的算法:

  • floyd,三重循环,时间复杂度O(n3n^3n3)。
  • dijkstra,循环起点进行单源最短路,单次时间复杂度O(mlogmmlogmmlogm),总时间复杂度O(nmlogmnmlogmnmlogm)。

显然,第2种写法时间复杂度更优,但他跑不了负边权呀,咋办?等着题目超时?就在这时,我们的johnson马上就要出场了,但我们还是得先把floyd讲完,不然蒟蒻编不下去了呀!

Flody

flody,编写起来容易,道理也是通俗易懂、简单粗暴。怎么说呢,你可以把它想成一个动态规划,dis[i][j]dis[i][j]dis[i][j]就代表iii点到jjj点的最短路长度,第一层循环就枚举间接节点,第二三层枚举iiijjj,那么动态转移方程就是dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j])

int dis[105][105];
for(int k=1;k<=n;k++){
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(k!=i&&i!=j&&k!=j)dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
		}
	}
}

Johnson

johnson,人如其名,听起来就是很厉害的样子。其实他就是dijkstra,啥?你没听错,不过是dijkstra的优化版,还参杂了一点bellman,由于dijkstra的短板是不能跑负边权图,所以我们就要把负边权图变成非负边权图。

  1. 我们先建立一个超级源点,把他跟每一个节点各连一条边权为0的边。//////让他指向其他节点

  2. 从源点出发,运行bellman,计算原点到每个结点的最短路径,用h数组统计。

  3. 对于每条边(u,v),把边权调整为w+h[u]-h[v],可以证明经过调整边权一定非负。

  4. 从每个节点出发,跑一边dijkstra,求出全源最短路,最后输出答案。
    这是张图

证明

我们可以把h想成一种势能,因为势能只跟起点和终点有关,所以dijkstra求出答案后只要减去h[u]加上h[v],就能得出最终结果。

如上图,假设我们要求1到3的最短路,先从1走到2,此时边权和为3+h[1]-h[2],接着再从2走到3,此时总边权为3+h[1]-h[2]-2+h[2]-h[3],结合起来就是h[1]-h[3]+1。

此时注意,这个结果仅仅只是dijkstra求出的结果,而我们想要是那个1,所以最终答案为dijkstra求出的结果再减去起点的势能再加上终点的势能。

接下来证明新图上的边权值均非负:

h是从超级源点出发到每个点的最短距离,所以对于任意一条边(u, v, w),一定有hv <= hu + w,上式移项得w + hu - hv >= 0,即新图中的边权非负。

实现

const ll Maxn=1e4,inf=1e9;
ll n,m,head[Maxn],tot,dis[Maxn],h[Maxn];
bool vis[Maxn];
ll cnt[Maxn];
struct edge1{
	ll u,v,w,Next;
}Edge[Maxn<<1]; 
inline void add(ll u,ll v,ll w){
	Edge[++tot]=(edge1){u,v,w,head[u]},head[u]=tot;
}
inline bool SPFA(ll s){
	for(ll i=1;i<=n;i++) h[i]=inf;
	h[s]=0;
	vis[s]=1;
	queue<ll>que;
	que.push(s);
	while(!que.empty()){
		ll u=que.front();
		que.pop();
		vis[u]=0;
		for(ll i=head[u];i;i=Edge[i].Next){
			ll v=Edge[i].v;
			if(h[u]+Edge[i].w<h[v]){
				h[v]=h[u]+Edge[i].w;
				if(!vis[v]){
					if(++cnt[v]>n+1)
						return 0;
					vis[v]=1;
					que.push(v);
				}
			}
		}
	}
	return 1;
}
struct node{
	ll v,num;
	bool operator <(const node &x)const{
		return num>x.num;
	}
};
inline void Dijkstra(ll s){
	for(ll i=0;i<=n;i++) dis[i]=inf,vis[i]=0;
	dis[s]=0;
	priority_queue<node>que;
	que.push((node){s,0});
	while(!que.empty()){
		ll u=que.top().v;
		que.pop();
		if(vis[u]) continue;
		vis[u]=1;
		for(ll i=head[u];i;i=Edge[i].Next){
			ll v=Edge[i].v;
			if(dis[u]+Edge[i].w<dis[v]){
				dis[v]=dis[u]+Edge[i].w;
				if(!vis[v]) que.push((node){v,dis[v]});
			}
		}
	}
} //该代码出自 知乎 作者秋钧《单源(全源)最短路及其应用》

完结撒花***\ (^ – ^) /***

您可能感兴趣的与本文相关的镜像

Llama Factory

Llama Factory

模型微调
LLama-Factory

LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值