floyd算法
适用范围:
>floyd算法主要用于求多(全)源最短路径的算法,可以适用于无向图和有向图,也可以用于负权的最短路径问题(虽然复杂度回比较高) >时间复杂度:O(n^3);空间复杂度:O(n^2);基本思想:
与Warshall算法一样,首先我们也要确定一个点k,但是这个点不是起始点,而是中间点。通过Floyd算法计算图G=(V,E)最短路径问题的时候,我们首先要引入邻接矩阵W来表示,用来计算每个相邻点的距离,W0也就是我们的已知条件
我们在进行Floyd算法的时候,不停的更新矩阵W。当我们根据某些规律(通常是从1到n)变化到中间点k的时候,W[i][j]代表从i到j仅用前k个点得到的最短路径,若W[i,j]>W[i,k]+W[j,k],则矩阵W中的W[i,j]需要改变成W[i,k]+W[k,j],完成从Wk-1到Wk的转换,到
当然,如果要求具体路径,可以用Path矩阵记录中转点
优点及其局限性
优点:
①. 思路简洁,代码实现容易,核心代码只有五行:
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(W[i][j]>W[i][k]+W[k][j])
W[i][j] = min(W[i][j], W[i][k]+W[k][j]);
}
}
}
②. 适合处理稠密图的多源路径问题
缺点:
①. 复杂度较高(O(n^3)),不适合处理单源路径问题
②. 对于存在“负权回路”(或者叫“负权环”)的图无能为力(因为带有“负权回路”的图没有最短路)
例如下面这个图就不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。
实战
1. 基础:最好的地方Best Spot
思路:要求出到F个给定点(牧场)的路径平均最短的点(牧场),只需要找出到这F个点(牧场)路径之和最短的点(牧场),只需要用floyd算法求出所有两两点(牧场)之间的最短路径,再通过枚举各个点(牧场)来得到路径和最小的那个点即可
AC代码:
#include <stdio.h>
#define MAX 100000000
int min(int a, int b)
{
return a<b?a:b;
}
int main(void)
{
int p, f, c; //p个点,f个给定点, c个(等价)关系
scanf("%d %d %d",&p,&f,&c);
int like[f+1], W[p+1][p+1];
for(int i = 1; i <= f; i++)
scanf("%d",like+i);
for(int i = 1; i <= p; i++)
{
for(int j = 1; j <= p; j++)
{
W[i][j] = MAX;
}
W[i][i] = 0; //自己到自己的路径费用显然应为0
}
int a, b, t;
for(int i = 1; i <= c; i++)
{
scanf("%d %d %d",&a,&b,&t);
W[a][b] = t;
W[b][a] = t; //等价关系的赋初值
}
for(int k = 1; k <= p; k++)
{
for(int i = 1; i <= p; i++)
{
for(int j = 1; j <= p; j++)
{
W[i][j] = min(W[i][j], W[i][k]+W[k][j]);
//W[j][i] = W[i][j];
//等i和j值互换时自然可以求出,故这里的W[j][i] = W[i][j]可加也可不加
}
}
}
int minnum = MAX, sum = 0, ans = 0;
for(int i = 1; i <= p; i++)
{
sum = 0;
for(int j = 1; j <= f; j++)
{
sum+=W[i][like[j]];
}
if(sum<minnum) //枚举各个点并比较
{
minnum = sum;
ans = i;
}
}
printf("%d
",ans);
return 0;
}
2. 变化:牛栏Cow Hurdles
题目意思是:找出i连通到j的途中最高的那个栏杆,我们想让这个最高的杆子尽量矮(小),如果不连通就输出-1
思路:由于求的是连通路径中所有栏杆最高的那个,那么首先肯定不能像以往一样把不连通的设置为无穷大(相比较其他的是无穷大就行),我这里是设置为-1,除此之外只要多加几个是否连通的判断即可
AC代码:
#include <stdio.h>
int min(int a, int b)
{
return a<b?a:b;
}
int max(int a, int b)
{
if(a==-1)
return b;
if(b==-1)
return a; //保证输出的是连通的那个
return a>b?a:b;//都连通再输出尽量高的栏杆
}
int main(void)
{
int n, m, t; //n个点, m个关系, t个询问
scanf("%d %d %d",&n,&m,&t);
int W[n+1][n+1];
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
W[i][j] = -1; //不连通默认赋值为-1,因为结果要求的是最高栏杆值,所以不能令不连通的路设置为无穷大
}
int s, e, h;
for(int i = 1; i <= m; i++)
{
scanf("%d %d %d",&s,&e,&h);
W[s][e] = h; //赋值初始邻接矩阵
}
for(int k = 1; k <= n; k++)
{
for(int i = 1; i <= n; i++)
{
for(int j = 1; j <= n; j++)
{
if(W[i][j]==-1) //如果仅仅用前k-1个点不能使i到j连通
{
if(W[i][k]!=-1&&W[k][j]!=-1) //并且通过中转点k可以连通的话
W[i][j] = max(W[i][k], W[k][j]);
}
else //如果仅仅用前k-1个点能使i到j连通
{
if(W[i][k]!=-1&&W[k][j]!=-1) //并且通过中转点k可以连通的话
W[i][j] = min(W[i][j], max(W[i][k], W[k][j]));
}
}
}
}
int a, b;
for(int i = 1; i <= t; i++) //对每个询问解答
{
scanf("%d %d",&a, &b);
printf("%d
",W[a][b]);
}
return 0;
}