首页 > 技术文章 > [总结]关于反向建图

Gaomez 2021-04-07 14:19 原文

今天做了这道题目.发现反向建图在某些情境下确实有各种特殊的效果.

当然,既然提到反向,下面讨论的图都是有向图.

 

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;
}
Silver Cow Party

 

2.正难则反

这是一个看起来比较宽泛的应用,举个例子.

P3916 图的遍历

给出一个有向图,求出每一个节点从自己出发能到达的编号最大的节点.数据达到了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;
}
P3916

 

先记下这么多.

推荐阅读