返回顶部

多源最短路径--flody算法

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]);
		}
	}
}

为什么k要在最外层?

②. 适合处理稠密图的多源路径问题

缺点:

①. 复杂度较高(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\n",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\n",W[a][b]);
    }
    return 0;
}

posted on 2019-09-19 14:51  进击の辣条  阅读(741)  评论(0编辑  收藏  举报

导航