[总结]关于反向建图
今天做了这道题目.发现反向建图在某些情境下确实有各种特殊的效果.
当然,既然提到反向,下面讨论的图都是有向图.
1.求最短路:从单到多变为多到单
上面提到的那道题目建立模型为:
给出一张有向图并指定其中的一个节点x,要求计算出一个点,满足从该点到x节点的最短路长度与从x节点到该节点的最短路长度之和最大.
利用Dijkstra(在这里可行的单源最短路算法)可以很容易算出从节点x到任意点的最短距离,现在需要求解从任意点到节点x的最短距离.
利用反向建图:
额外建立一个有向图,该图中所有的边都与原图方向相反,权值不变.
在这张图中再次使用Dijkstra求出从节点x到任意点的最短距离.
在这张图上得到的最短距离dist[i]就是在原图上从节点i到节点x的最短距离.
参考:
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <queue> using namespace std; struct E{ int to, wei; bool operator<(const E &other) const {return wei > other.wei;} }; vector<E> e1[1010], e2[1010]; priority_queue<E> q; int n, m, x, dist1[1010], dist2[1010]; int main(){ scanf("%d%d%d", &n, &m, &x); while(m--){ int a, b, t; scanf("%d%d%d", &a, &b, &t); e1[a].push_back({b, t}); e2[b].push_back({a, t}); } // from x to home memset(dist1, -1, sizeof(dist1)); q.push({x, 0}); while(!q.empty()){ E cur = q.top(); q.pop(); if(dist1[cur.to] != -1) continue; dist1[cur.to] = cur.wei; for(int i = 0; i < e1[cur.to].size(); i++) q.push({e1[cur.to][i].to, e1[cur.to][i].wei + cur.wei}); } // from home to x memset(dist2, -1, sizeof(dist2)); q.push({x, 0}); while(!q.empty()){ E cur = q.top(); q.pop(); if(dist2[cur.to] != -1) continue; dist2[cur.to] = cur.wei; for(int i = 0; i < e2[cur.to].size(); i++) q.push({e2[cur.to][i].to, e2[cur.to][i].wei + cur.wei}); } int ans = 0; for(int i = 1; i <= n; i++) ans = max(ans, dist1[i] + dist2[i]); printf("%d\n", ans); return 0; }
2.正难则反
这是一个看起来比较宽泛的应用,举个例子.
给出一个有向图,求出每一个节点从自己出发能到达的编号最大的节点.数据达到了1e5.
如果让每个节点独立地找太慢了,反过来,让编号最大的节点去找能遍历到它的节点.
反向建图,从编号最大的N号节点开始,依次对N,N-1,N-2...号节点执行如下操作:
遍历其子节点,对所有未标记的节点标上其编号并(递归地)遍历该节点的子节点.
参考:
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> #include <vector> using namespace std; int n, m; vector<int> s[100010]; int ans[100010]; void dfs(int x, int k) { if (ans[x]) return; ans[x] = k; for(auto i: s[x]) dfs(i, k); } int main() { cin >> n >> m; while(m--) { int x, y; cin >> x >> y; s[y].push_back(x); } for (int i = n; i >= 1; i--) dfs(i, i); for (int i = 1; i <= n; i++) cout << ans[i] << ' '; return 0; }
先记下这么多.