图论--最短路之--Floyd

  • Floyd:稳定的O(n^3)复杂度,一般只在节点数 < 300 时候考虑。除了求最短路,还可以用来判断图的连通性。

基本结构很简单:最外层遍历中转节点,内层遍历两端点,具体为什么可参见我的另一条博客。最近做题遇到了几道用Floyd的题目,又有了新的理解:设uv为两端点,k为中转节点,那么Floyd的遍历方式其实就是先只允许以1号节点进行中转,接着允许以1和2号节点进行中转,再然后允许以1、2、3号节点进行中转……最后允许1-n号节点进行中转,求最短路。每次在枚举k时,得到的是目前经过前k个节点的最短路径。

题目:灾后重建

这道题典型的floyd解法,但是光靠模板是过不了滴。一开始我用的是dijkstra,超时了,后来发现节点数不多才采用floyd。题目已经说明访问的t是非递减的,那么就无需对节点进行排序,只需要每次遍历到未超时的中转节点即可,下一次遍历直接接着上一次的节点继续往后遍历即可。

//数据量不大-->floyd。
//每当看到节点数<300时都可以考虑floyd
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
#define ll long long 
#define Inf 0x3f3f3f3f
const int maxv = 200 + 2;
int pt[maxv], n, m, a, b, c;
ll table[maxv][maxv];
int Q, x, y, t, k;

bool floyd(const int& st, const int& en, const int& tt){
    if(pt[st] > tt || pt[en] > tt) return false;
    while(pt[k] <= tt && k < n){
        for(int u=0; u<n; ++u){
            for(int v=0; v<n; ++v){
                if(u == v || u ==k || v == k) continue;
                // if(pt[u] > tt || pt[v] > tt) continue;//想想为什么这句不要?
                table[u][v] = min(table[u][v], table[u][k] + table[k][v]);
            }
        }
        k += 1;
    }
    return table[st][en] != Inf;
}

inline int read(){
    char ch = getchar();
    int ans = 0, tool = 1;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9'){
        ans = (ans << 3) + (ans << 1) + ch - '0';
        ch = getchar();
    }return (ans * tool);
}
int main()
{
    n = read(), m = read();
    for(int i=0; i<n; ++i)    //table只需要初始化一次,因为之后的t都是增加的
        for(int j=0; j<n; ++j)
            table[i][j] = Inf;
    for(int i=0; i<n; ++i)
        pt[i] = read();
    while(m--){
        a = read(), b = read(), c = read();
        table[a][b] = table[b][a] = c;
    }
    Q = read();
    while(Q--){
        x = read(), y = read(), t = read();
        if(floyd(x, y, t)){
            printf("%lld\n", table[x][y]);
        }
        else puts("-1");
    }
    return 0;
}
View Code

 再放一个要对k节点进行排序的,之前也放过。题目:牛收费

这道题要先将节点权值较小的先遍历,越大的越后遍历,为何这样我现在有些似懂非懂,不敢乱说,先放这给我几天再想想,若有高人能给我指点更好不过。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define Inf 0x3f3f3f3f
#define ll long long
const int maxn = 250 + 4;
struct Node{
    int w, id;
}node[maxn];
ll table[maxn][maxn], dis[maxn][maxn];
int n, m, k, u, v, w, st, en;
int newid[maxn];
inline int read(){
    char ch = getchar();
    int ans = 0;
    while(ch < '0' || ch > '9') ch = getchar();
    while(ch >= '0' && ch <= '9'){
        ans = (ans << 3) + (ans << 1) + ch - '0';
        ch =getchar();
    }return ans;
}
inline bool comp(const Node& a, const Node& b){
    return a.w < b.w;
}

inline void floyd(){
    for(int k=1; k<=n; ++k){
        for(int i=1; i<=n; ++i){
            for(int j=1; j<=n; ++j){
                if(i == j || i == k || j == k) continue;
                table[i][j] = min(table[i][j], table[i][k] + table[k][j]);
                dis[i][j] = min(dis[i][j], table[i][j] + max(node[k].w, max(node[i].w, node[j].w)));
            }
        }
    }
    return;
}
//涉及到最短路和最短路上的最大节点,用两个数组分别存储
//由于floyd中间节点由前往后遍历,所以我们把权值越大的点越往后遍历
//使得最终遍历到k==n时,保证如果选取中间节点选取的节点是所有中间节点中权值最大的
int main()
{
    memset(table, Inf, sizeof(table));
    memset(dis, Inf, sizeof(dis));
    n = read(), m = read(), k = read();
    for(int i=1; i<=n; ++i) 
        {node[i].w = read(), node[i].id = i, table[i][i] = 0;}
    sort(node+1, node+n+1, comp); //节点按权值升序
    for(int i=1; i<=n; ++i)
        newid[node[i].id] = i; 
        //相当于把我想要优先遍历的序号排在前边
        //这样在floyd中的遍历就更加方便注意之后的存图位置也要用newid数组

    for(int i=1; i<=m; ++i){
        u = read(), v = read(), w = read();
        table[newid[u]][newid[v]] = table[newid[v]][newid[u]] = min(table[newid[u]][newid[v]], (ll)w);
        //为方便之后的floyd访问,用newid【】数组来获取新的顺序编号
        //转换之后,权值较小的节点会被优先遍历到
        //注意可能会有重边,所以要有一个min()判断一下
    }

    floyd();
    while(k--){
        st = read(), en = read();
        printf("%lld\n", dis[newid[st]][newid[en]]);
    }
    return 0;
}
View Code
posted @ 2019-07-09 19:33  Bankarian  阅读(141)  评论(0编辑  收藏  举报