解最短路
复杂度O(n3)
伪码:
for (k = 0; k < n; k++)
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
if (dis[i, j] > dis[i, k] + dis[k, j])
dis[i, j] = dis[i, k] + dis[k, j]
接触Floyd有一段时间了,但是对Floyd的印象就是,只记得它可以求任意两点间的最短距离,有三层循环,也不会用。。。。。
今天总结的时候看到挑战程序设计竞赛中的讲解
看到那个“三维数组”d,当时就懵了。。。。。
然后翻博客也只明白了,如果要让i、j两个点之间的距离变短,可以引入一个中转点k:i—>k—>j,或者多个中转点k1、k2:i—>k1—>k2—>j
直到后面看见啊哈里面的一句话:
对,因为我们存的是邻接矩阵,所如果只能通过一个顶点来中转的话,值需要遍历一下dis数组(dis[i][j]表示顶点i到j的距离,并且d[i][j]的值在循环的过程中不断优化),判断是否可以通过1号点中转来获得更短的路径,执行一下dis[i][j] = min(dis[i][j], dis[i][1] + dis[1][j])
然后的操作(有点像二维数组的01背包,都是在前面的基础上进行操作,当枚举顶点k之前我们已经求得了只有顶点1~k-1作为中转点时的最短路)
直到最后能通过所有点中转就能得到任意两点之间的最短距离。
然后画了个图理解下算法最终是怎么得到最短路径的:
(只看2到3的最短距离,最开始边权都存在dis数组里面,dis[2][3]=103)
k=5时,可以通过5更新dis,dis[2][3]=23+34;k=7时,可以通过7更新dis,就是7到2的dis值+7到3的dis值=3+(12+34),然后我们会发现点7加进来时,就有:2到7的最短路径+7到3的最短路径=2到3的最短路径,而后面这两个最短路径已经在点7加入之前更新完成了;如果还有第8个点可以更新dis[2][3],那么肯定也有:2到8的最短路径+8到3的最短路径=2到3的最短路径,所以这样遍历下去的话我们就可以得到最终的最短路径。
求最短路的代码:Floyd算法(多源最短路)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 105;
const int INF = 0x3f3f3f3f;
int dis[maxn][maxn];
///dis[i][j]表示 i到j的最短距离
void init() {
for (int i = 1; i < maxn; i++) {
for (int j = 1; j < maxn; j++)
dis[i][j] = INF;
dis[i][i] = 0;
}
}
int main() {
init();
int n, m, s, e, c;
scanf("%d %d", &n, &m);
while (m--) {
scanf("%d %d %d", &s, &e, &c);
dis[s][e] = min(dis[s][e], c);//可能存在重边
// 无向边 dis[e][s]=min(dis[e][s], c);
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
//这个过程中,dis[i][j]的值都是i到j的路径不断优化的结果
return 0;
}
最小环(无向边)
参考了dalao的博客:Floyd最小环算法
问题描述: 给你一张无向图,定义环为从i出发到达j然后从j返回i并且所有点都只经过一次(最少为3个点),求所有环当中经过路径最小的环
算法描述:
首先容易想到的是暴力来枚举环,当删除其中一条边ij后再跑一边从i到j的最短路,然后加上边ij的值就是含有边ij的最小环的值,这样最坏的时间复杂度可以达到O(n4),显然复杂度有点大。(再准确点来说复杂度是O(e*n2),e是图中不重复边的条数)
所以我们考虑降低时间复杂度,因为要保证最少三个点,所以我们可以枚举任意三个不同的点,然后看其中两个点有0个、1个、2个、3个、、、中转点时可能会更新的最短路径长度。
我们来分析下floyd的实现过程,当枚举顶点k之前我们已经求得了顶点为1 - k-1 的最短路,所以我们可以在跟新k之前枚举k之前的i和j的组合
我们可以知道dis[i][j]没有经过k点,所以我们就可以知道如果dis[i][j]+mp[i][k]+mp[k][j] != inf(mp[i][j]为没有跟新得边值) 时就存在一条经过ijk的最小环,所以我们要求的是所有环当中最小的哪一个!
HDU1599:
#include<bits/stdc++.h> //以hdu1599为例
using namespace std;
#define INF 1e8
int e[105][105];
int dis[105][105];
int main()
{
int n,m,s,e,len,dis[200][200],mp[200][200];
while(cin>>n>>m)
{
int mmin=INF;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
if(i==j)
dis[i][j]=mp[i][j]=0;
else
dis[i][j]=mp[i][j]=INF;
while(m--)
{
scanf("%d%d%d",&s,&e,&len);
dis[s][e]=mp[s][e]=dis[e][s]=mp[e][s]=min(len,mp[s][e]);
}
for(int k=1; k<=n; k++)
{
for(int i=1; i<k; i++)
for(int j=1; j<k; j++)//有的代码初始化的=i+1,主要就是为了保证i!=j
if(i!=j)
mmin=min(mmin,dis[i][j]+mp[i][k]+mp[k][j]);
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
if(mmin==INF)
printf("It's impossible.\n");
else
printf("%d\n",mmin);
}
return 0;
}
最小环(有向边)
上面的代码也可以解决有向边的情况,只是还需要改一下里面边得起始点,或者直接跑一边Floyd最短路,然后遍历一遍dis[i][j]+mp[j][i]求最小环
有向边:关于Floyd求解最小环的问题