最短路算法(floyd)

最短路算法(floyd)

最短路性质

在最短路中对于边权为正的图
任意两个节点之间的最短路
不会:
经过重复的节点
不会经过重复的边
节点数不会超过 \(n\) 边数不超过 \(n-1\)
这三条性质指向同一个问题,没有负边就不需要有额外的步数去减小路径和

floyd

求解任意两节点的最短路,适用于任何图,但是图上不能有负环(不能判断负环)

时间复杂度为 \(n^{3}\) ,优点是实现简单

\(f_{k,x,y}\)为只用前 1 到 k 个节点,从 \(x\)\(y\) 的最短路,那么

显然当 k 等于 n 时是最终答案,考虑如何转移

初始化 k 等于 0 时的值

分三类情况讨论

当 x y 不直接相连时,显然应该为正无穷

当 x y 直接相连,就是边权

当 x y 相等,就为 0

接着转移,当 k 增大时

若选择不经过当前 k 这个节点,那么路径的值就不会改变,那么要使值得到更新那就是计算经过 k 的最短路

那么状态转移方程式就是

f[k][x][y] = min(f[k-1][x][y], f[k-1][x][k]+f[k-1][k][y])(f[k-1][x][y]

由于只与前一步有关,第一维就没有用了

所以最后有代码

for (k = 1; k <= n; k++) {
  for (x = 1; x <= n; x++) {
    for (y = 1; y <= n; y++) {
      f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
    }
  }
}

P6175 无向图的最小环问题 - 洛谷

最短路变为了最短环,我们只需要稍微变换一下,我们发现在不选 \(k\) 时的 \(k-1\) 状态下再多选一个 k 可以有构成环的情况,用 ans 来统计答案则为

ans=min(ans,f[i][o]+dis[i][k]+dis[k][o]);

其中只需要用 i o 之间的最短路加上到 k 的距离就可以了

注意初始化,先统计答案再更新,因为是从没选 k 的状态来统计的

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=105;
int n,m;
int dis[N][N];
int f[N][N];
int ans=1e18;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);    cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int o=1;o<=n;o++){
			if(i!=o){
				dis[i][o]=f[i][o]=1e18;
			}
		}
	}
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		dis[u][v]=min(dis[u][v],w);
		dis[v][u]=min(dis[v][u],w);
		f[v][u]=min(f[v][u],w);
		f[u][v]=min(f[u][v],w);
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int o=1;o<=n;o++){
				if(i!=o&&dis[i][k]&&dis[k][o])ans=min(ans,f[i][o]+dis[i][k]+dis[k][o]);
				f[i][o]=min(f[i][o],f[i][k]+f[k][o]);
			}
		}
	}
	if(ans==1e18) cout<<"No solution.";
	else cout<<ans; 
	return 0;
} 
posted @ 2025-07-21 11:11  Zom_j  阅读(15)  评论(0)    收藏  举报