floyd为什么可行?

总结

最开始,看到这个算法,感觉代码简洁,好用,就只是把这个板子记住了,但是发现这样的习惯真心不好,所以现在想理解一下
首先

空间复杂度:O(n2

时间复杂度:O(n3

作用:多源最短路径

注意:可以处理正负权值,但是不可以处理-负权回路(只会越减越小)-。
当时看了一下这两位大佬的博客
博客园 CSDN

这个算法,我开始有一个疑问,为什么dp[i][j]=min(dp[[i][j],dp[i][k]+dp[k][j]),这样转移一下,就是可行的?

万一当前转移的时候,dp[i][k]和dp[k][j]都不是最小的,这样转移的话,dp[i][j]就不是最小的,最终却是可行的!
在这里插入图片描述
首先A:

memset(dp,inf,sizeof(dp));
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)
                dp[i][j]=0;

邻接矩阵初始化,自环权值为0,无边权值为无穷大
然后B:

    while(m--)
    {
        int x,y,v;
        cin>>x>>y>>v;
        dp[x][y]=min(dp[x][y],v);
    }

把每一条边的权值更新一下,min的作用,避免一条重复多边,因为最短路径只需要最小的边权值

最后C:

    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                dp[i][j]=min(dp[i][j],dp[i][k],dp[k][j]);

枚举一个图的所有两点是n^2,也就是第二层和第三层循环的作用
在这里插入图片描述

这里我特别提一下第一层循环
这个循环核心用了中转点
k=0,也就是没有中转点
这里就是上面==然后B:==边权值更新,两点最短路径没有任何中转点,权值就是填进去的权值

开始第一层for k循环

k=1,也就是只有1作为中转点
这里会发生什么呢?
两结点之间只有1作为中转点,上面图中更新最小边权值或者添加边
在这里插入图片描述
如果dp[2][4]边权值为无穷大,就图中添加一个边
如果dp[2][4]边权值非无穷大,就图中更新min边权值
k=1的2 3循环结束后,两结点最短路径之间,中转点要么没有,要么中转点为1
如果不考虑1结点与其他结点,在图中可以把结点1删除了看边权值更加直观

k=2,也就是以2作为中转点(但是这里中转点可能为2,也可能为1哦,也可能为-1-2-,也能没有)
这里会发生什么呢?
我们会发现在求dp[4][3]时,dp[4][2]和dp[2][3],已经是最小的了,为什么会这样?
如果这个图,结点更多,4->3之间有其他中转点更小边权值,这个是不是就不一定最小的了呢
什么情况:dp[4][2]和dp[2][3]这里的最小
只是dp[4][2]中无中转点或者只有中转点1时,是最小路径
同理dp[2][3]中无中转点或者只有中转点1时,是最小路径

k=3,也就是以3作为中转点(但是这里中转点S={3,2,1,3-2,3-1,2-1,3-2-1,空集})
这层同理了,这层进行结束,就代表上诉存在中转点情况中,多源最短路径最小

k=4
k=5
直到k=n
当k=n是,就代表所有情况中转点都考虑,所以可行的

    for(int k=1;k<=n;k++)
    for(int k=n;k>=1;k--)用上面跑,跟用下面的跑一样的

每跑一个结点作为中转点,就代表之前的跑的中转点可能作为中转点

dp[i][j]=min(dp[[i][j],dp[i][k]+dp[k][j])

回到最初的问题,floyd为什么可行?

就是每跑一个结点作为中转点,就代表之前的跑的中转点都可能作为最短路径的中转点,每一步都是最优的,只不过过程跑的不是全局最优,跑完后,就是最局最优了

发布了130 篇原创文章 · 获赞 5 · 访问量 5009

猜你喜欢

转载自blog.csdn.net/weixin_44224825/article/details/104094817