Floyd-Warshall算法及例题

概要

Floyd可以一次性求出所有节点之间的最短距离

  • 这种算法主要采用了动态规划的思想,假设求从i到j的最短路径,那么寻找一个中间位置k,如果从i到k距离加上k到j的距离比直接从i到j小,那么就更新从i到j的最短路径
  • 根据上面的设想,Floyd是由三层for循环组成的,第一层for循环显然是k,因为我们不知道谁来借助k,所以k应该放在最外面;里面的两层分别是i和j,这不难理解。

朴素的Floyd如下

void Floyd(int n){
    
    
    for(int k=1;k<=n;k++){
    
    
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=n;j++){
    
    
                if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
            }
        }
    }
}
  • 可以看出Floyd算法时间复杂度是O(n3),只能解决一些小图的最短路问题,但是代码书写简单,且能一次性求出所有边的最短路

练习

hdu2544

hdu2544
这道题可用floyd,不加任何优化,旨在熟悉算法框架

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 500;
int INF = 0x3f3f3f3f;
int edge[MAXN][MAXN];
void Floyd(int n){
    
    
    for(int k=1;k<=n;k++){
    
    
        for(int i=1;i<=n;i++){
    
    
            for(int j=1;j<=n;j++){
    
    
                if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
            }
        }
    }
}
int main(){
    
    
    int n,m,a,b,c;
    while(cin>>n>>m){
    
    
        if(n == 0&&m == 0) break;
        memset(edge,0x3f,sizeof edge);
        for(int i=0;i<m;i++){
    
    
            cin>>a>>b>>c;
            edge[a][b] = min(edge[a][b],c);
            edge[b][a] = edge[a][b];
        }
        Floyd(n);
        cout<<edge[1][n]<<endl;
    }
    return 0;
}

hdu3631

hdu3631

  • 题目要求借助标记过的点寻找最短路,显然是floyd算法思想,又n范围不大,考虑使用floyd
  • 需要注意自己到自己的距离问题,需要设置为0,输出格式注意一下
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+100;
int edge[500][500];
int vis[MAXN];
const int INF = 0x3f3f3f3f;
void floyd(int k,int n){
    
    
    for(int i=0;i<n;i++){
    
    
        if(edge[i][k]!=INF)
        for(int j=0;j<n;j++){
    
    
            if(edge[i][j] > edge[i][k] + edge[k][j]){
    
    
                edge[i][j] = edge[i][k] + edge[k][j];
            }
        }
    }
}
int main(){
    
    
    int n,m,q,a,b,c,op;
    int times = 1;
    while(~scanf("%d%d%d",&n,&m,&q)){
    
    
        if(n == 0&&m == 0&&q == 0) break;
        if(times != 1) printf("\n");
        memset(edge,0x3f,sizeof edge);
        memset(vis,0,sizeof vis);
        for(int i=0;i<n;i++) edge[i][i] = 0;
        for(int i=0;i<m;i++){
    
    
            scanf("%d%d%d",&a,&b,&c);
            if(edge[a][b] > c) edge[a][b] = c;
        }
        printf("Case %d:\n",times++);
        while(q--){
    
    
            scanf("%d",&op);
            if(op == 0){
    
    
                scanf("%d",&a);
                if(vis[a]) {
    
    
                    printf("ERROR! At point %d\n",a);
                }
                else {
    
    
                    vis[a] = 1;
                    floyd(a,n);
                }
            }else{
    
    
                scanf("%d%d",&a,&b);
                if(!vis[a]||!vis[b]) printf("ERROR! At path %d to %d\n", a, b);
                else if(edge[a][b] == INF) printf("No such path\n");
                else printf("%d\n",edge[a][b]);
            }
        }
    }
    return 0;
}

hdu1704

hdu1704

  • 题目意思是给出了若干比赛关系,问不能确定的冠军数量
  • 抽象成一个图,那么很明显如果两个点之间可达,不论顺序,那么他们之间就能确定谁是冠军

此题是使用floyd算法求图的传递闭包,有关传递闭包概念如下
在这里插入图片描述

  • 所以直接使用floyd算法,如果从i到j和j到i都没有路径,则构成一个答案
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
using namespace std;
typedef long long ll;
const int MAXN = 2e5+100;
int edge[600][600];
const int INF = 0x3f3f3f3f;
void floyd(int n){
    
    
    for(int k=1;k<=n;k++){
    
    
        for(int i=1;i<=n;i++){
    
    
            if(edge[i][k] != INF)
            for(int j=1;j<=n;j++){
    
    
                if(edge[i][j] > edge[i][k] + edge[k][j]){
    
    
                    edge[i][j] = edge[i][k] + edge[k][j];
                }
            }
        }
    }
}
int main(){
    
    
    int t,n,m,x,y;
    cin>>t;
    while(t--){
    
    
        memset(edge,0x3f,sizeof edge);
        cin>>n>>m;
        for(int i=0;i<m;i++){
    
    
            cin>>x>>y;
            edge[x][y] = 1;
        }
        floyd(n);
        int ans = 0;
        for(int i=1;i<=n;i++){
    
    
            for(int j=i+1;j<=n;j++){
    
    
                if(edge[i][j] == INF&&edge[j][i] == INF){
    
    
                    ans++;
                }
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

洛谷P1119

题目链接

  • 看数据范围应该是floyd。道路不需要修,而村庄需要修,每修一个村庄相当于图中加一个点,如果每加一个点就跑一次floyd那么显然不合理,题目给出村庄修复时间是升序,这是很方便的,可以考虑每次加的点作为floyd中的中介点k,按照规定时间范围遍历,如果x和y能够借助k互相达到,那么更新最短路,这个时候不需要考虑x或者y是否修好,因为如果没修好就输出-1,但是最短路需要更新
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iomanip>
#include <queue>
#include <stack>
#include <sstream>
#include <map>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int MAXN = 2e5+100;
int Data[MAXN];
int edge[300][300];
int TIME[MAXN];
int Floyd(int n, int x, int y, int t, int &k){
    
    
    for(;k<n;k++){
    
    
        if(t < TIME[k]) break;
        for(int i=0;i<n;i++){
    
    
            if(edge[i][k] != INF){
    
    
                for(int j=0;j<n;j++){
    
    
                    if(edge[i][j] > edge[i][k] + edge[k][j]) edge[i][j] = edge[i][k] + edge[k][j];
                }
            }
        }
    }
    if(t < TIME[x] || t < TIME[y]) return -1;
    if(edge[x][y] == INF) return -1;
    else return edge[x][y];
}
int main(){
    
    
    int n,m,q,x,y,w,t;
    int k = 0;
    cin>>n>>m;
    memset(edge, 0x3f, sizeof edge);
    for(int i=0;i<n;i++) cin>>TIME[i];
    for(int i=0;i<m;i++){
    
    
        cin>>x>>y>>w;
        if(w < edge[x][y]) edge[x][y] = edge[y][x] = w;
    }cin>>q;
    for(int i=0;i<q;i++){
    
    
        cin>>x>>y>>t;
        cout<<Floyd(n,x,y,t,k)<<endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/roadtohacker/article/details/112768253