最短路算法(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;
}

浙公网安备 33010602011771号