- 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; }
再放一个要对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; }