基本思想:动态规划.又称插点法。
//完美的博客
https://blog.csdn.net/anlian523/article/details/80925625#%E6%97%A0%E8%B4%9F%E6%9D%83%E7%8E%AF

算法过程:
将求最短路分阶段进行.
最开始图中两个点,假设只能经过3号点,暴力枚举看是否能够更新dp[i][j].
3号点更新完之后,再将4号点***来,继续去更新dp[i][j]
如此往复,将所有点都入图中,最后求出dp[i][j].

解释:
①在插入第k个点的时候dp[i][j]的含义为i 到 j 只含前k个点的最短路.
应用:求最短路,最长路,传递闭包,求最小环
时间复杂度:O(n^3)
存储:邻接矩阵

代码实现:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int inf = 0x3f;
const int maxn = 1005;
int f[maxn][maxn];
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    int x,y,z;
    memset(f,inf,sizeof f);
    for(int i = 1;i<=n;i++)
          f[i][i] = 0;
    for(int i = 1;i<=m;i++)
    {
        cin>>x>>y>>z;
        f[x][y] = f[y][x] = z;
    }
    for(int k = 1;k<=n;k++)
        for(int i = 1;i<=n;i++)
            for(int j = 1;j<=n;j++)
                if(f[i][k] + f[k][j] < f[i][j])
                    f[i][j] =  f[i][k] + f[k][j];
    int q;
    cin>>q;
    //int x,y;
    while(q--)
    {
        cin>>x>>y;
        cout<<f[x][y]<<endl;
    }
    return 0;
}

拓展:Floyd求最小环(待补)

对于一个最小环,显然至少要包含三个点.
算法进行到第k阶段时,
对于图中所有点求 ans = min(ans,f[i][j] + dis[i][k]+dis[k][j]).
若图中有过k点的环,那么本阶段ans求得的是只允许松弛前k个点的最小环。
我们也只需要用k去更新前k-1个点就好.

证明(口胡):
假设图中没有环,那么ans为INF。
假设图中有m个环,
设为 c1 c2 c3 c4 ... cm.
其中 Cx为权值最小环.设Cx中点编号最大点为***,设环中直接与***相邻的两个点为i,j;
那么当进行到第G个阶段, ans = min(ans,f[i][j] + dis[i][G]+dis[G][j]).
此时f[i][j]已经是全局最短路(因为***是环中编号最大的点了).
所以求得的就是Cx的值,此后值不再变化,得证。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int inf = 0x3f;
const int maxn = 1005;
int f[maxn][maxn],dis[maxn][maxn];
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    int x,y,z;
   // memset(f,inf,sizeof f);   //注意 memset的inf*3 会爆int
    for(int i = 1;i<=maxn;i++)
        for(int j = 1;j<=maxn;j++)
            if(i == j) f[i][j] = 0;
            else f[i][j] = 9e7;
    for(int i = 1;i<=m;i++)
    {
        cin>>x>>y>>z;
        f[x][y] = f[y][x] = z;
        dis[x][y] = dis[y][x] = z;
    }
    int ans;
    memset(ans,inf,sizeof ans);
    for(int k = 1;k<=n;k++)
    {
        for(int i = 1;i<=k-1;i++)
            for(int j = i+1;j<=k-1;j++)
                    ans =  min(ans,f[i][j] + dis[i][k]+dis[k][j]);
        for(int i = 1;i<=n;i++)
            for(int j = 1;j<=n;j++)
                if(f[i][k] + f[k][j] < f[i][j])
                    f[i][j] =  f[i][k] + f[k][j];
    }
    int q;
    cin>>q;
    //int x,y;
    /*while(q--)
    {
        cin>>x>>y;
        cout<<f[x][y]<<endl;
    }*/
    return 0;
}


//

/*
4 5
1 2 1
2 4 1
1 4 10
1 3 1
3 4 20

*/

①无向图注意要处理重边,若有重边出现,取min
②求最小环的时候只能循环到k-1.

拓展二:Floyd链表判圈(面试)