您的位置:首页 > 其它

tarjan求割顶和桥 hihoCoder1183 连通性一·割边与割点

2015-07-24 21:08 573 查看
终于把tarjan算法理解了

整个tarjan算法有两个非常重要的数组,一个是Low一个是DFN

DFN数组是个时间戳,说白了就是访问那个节点的时间,也就是第几次调用DFS这个函数

Low是u的子节点能通过反向边到达的节点DFN的最小值,初始值为DFN[u]

那么如何判断是否是割顶和割边呢



对于上面这个建图,假如我们最开始的根节点是1,然后会得到这样的数组

id  1 2 3 4 5 6
dfn 1 2 3 4 5 6
low 1 1 1 4 4 4


对于割顶,个人认为可以分成3种情况

第一种情况,是对我们DFS树的根节点,对于我刚刚说的,我们一开始访问的是1,那么 1就是根节点,

这个点是不是割顶呢?这要看它儿子的数量,如果>=2,那么把这个点拆了,必然会有增加的连通分量。如果等于1,那么拆了也不会有增加的连通分量,所以儿子数量等于1,就不是割顶,大于1就是割顶

第二种情况,对于某个点u,设其儿子为v,如果存在一个v,使得low[v] >dfn[u] ,在上面这个样例里面,有存在的情况,u=3,v=4就是满足这种情况的

第三种情况,对于某个点u,设其儿子为v,如果存在一个v,使得low[v]==dfn[u,在上面这个样例里面,有存在的情况,u=4,v=5就是满足这种情况的

对于桥,就是割顶的第二种情况

对于某个点u,设其儿子为v,如果存在一个v,使得low[v] >dfn[u] ,在上面这个样例里面,有存在的情况,u=3,v=4就是满足这种情况的,那么说明v怎么也回不到u的父节点去,所以此时u-v这条边就是桥

#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<functional>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 2e5 + 5;
const int INF = 0x3f3f3f3f;

int rear;
int Head[MX], Next[MX];
int Low[MX], DFN[MX], dfs_clock;
int cut[MX];

struct Edge {
    int u, v, sign;
} E[MX];

void edge_init() {
    rear = 0;
    memset(Head, -1, sizeof(Head));
    memset(Next, -1, sizeof(Next));
}

void edge_add(int u, int v) {
    E[rear].u = u;
    E[rear].v = v;
    E[rear].sign = false;
    Next[rear] = Head[u];
    Head[u] = rear++;
}

int tarjan(int u, int from) {
    Low[u] = DFN[u] = ++dfs_clock;

    int child = 0;
    for(int id = Head[u]; ~id; id = Next[id]) {
        int v = E[id].v;

        if(!DFN[v]) {
            int lowv = tarjan(v, u);
            Low[u] = min(Low[u], lowv);

            if(lowv >= DFN[u]) {
                cut[u] = 1;
            }
            if(lowv > DFN[u]) {
                E[id].sign = 1;
                E[id ^ 1].sign = 1;
            }

            child++;
        } else if(v != from) {
            Low[u] = min(Low[u], DFN[v]);
        }
    }

    if(from == -1 && child == 1) cut[u] = 0;
    return Low[u];
}

int main() {
    int n, m;
    scanf("%d%d", &n, &m);

    dfs_clock = 0;
    edge_init();
    memset(DFN, 0, sizeof(DFN));
    memset(cut, 0, sizeof(cut));

    for(int i = 1; i <= m; i++) {
        int u, v;
        scanf("%d%d", &u, &v);
        edge_add(u, v);
        edge_add(v, u);
    }

    tarjan(1, -1);//如果已经确定是连通图,就这样写
    /*
    否则要这样
    for(int i = 1; i <= n; i++) {
        if(!DFN[i]) tarjan(1, -1);
    }
    */
    int cnt = 0, first = true;
    for(int i = 1; i <= n; i++) {
        if(cut[i]) {
            cnt++;

            if(first) first = false;
            else printf(" ");
            printf("%d", i);
        }
    }
    printf("%s\n", cnt ? "" : "Null");

    vector<PII>ans;
    for(int i = 0; i < rear; i++) {
        if(E[i].sign && E[i].u < E[i].v) {
            ans.push_back(PII(E[i].u, E[i].v));
        }
    }
    sort(ans.begin(), ans.end());
    for(int i = 0; i < ans.size(); i++) {
        printf("%d %d\n", ans[i].first, ans[i].second);
    }
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: