Tarjan算法-强连通分量、LCA(poj1144)

Tarjan算法

持续补充

首先适用于无向图
在强连通分量中已经得到了每一个点的low、dfn,

输入指向关系的顺序不同得到的每个点的dfn[]、low[]的值不同,(遍历到的次序不同),然后观察,
发现


  • 割点
    (u为当前点v为下一个相邻的点)当dfn[u] <= low[v]时当前的节点u为一个割点
    分析:v为子树,然后low[v] >= dfn[u] 代表子树不存在一个路径可以时他到达u的祖先,所以u以及u一下的v……这个整体可以组成一个块,所以u是割点
//
//  main.cpp
//  tarjan_求割点
//
//  Created by 陈冉飞 on 2019/8/16.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

//#include<bits/stdc++.h>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 100086;
struct node {
    int y, net;
}e[maxn << 1];
int lin[maxn], len = 0;
int n, m, root, num = 0;
int dfn[maxn], low[maxn];
bool cut[maxn];

inline int read() {
    int x = 0, y = 1;
    char ch = getchar();
    while(!isdigit(ch)) {
        if(ch == '-') y = -1;
        ch = getchar();
    }
    while(isdigit(ch)) {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    return x * y;
}

inline void insert(int xx, int yy) {
    e[++len].y = yy;
    e[len].net = lin[xx];
    lin[xx] = len;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++num;
    int flag = 0;
    for(int i = lin[x]; i; i = e[i].net) {
        int to = e[i].y;
        if(!dfn[to]) {
            tarjan(to);
            low[x] = min(low[x], low[to]);
            if(low[to] >= dfn[x]) {
                flag++;
                if(x != root || flag > 1) cut[x] = true;
            }
        }
        else low[x] = min(low[x], dfn[to]);
    }
}

int main() {
    n = read(), m = read();
    len = 1;
    for(int i = 1; i <= m; ++i) {
        int x, y;
        x = read(), y = read();
        if(x == y) continue;
        insert(x, y);
        insert(y, x);
    }
    for(int i = 1; i <= n; ++i)
        if(!dfn[i]) {
            root = i;
            tarjan(i);
        }
    //输出dfn low
    for (int i = 1; i <= n; i++) cout<<i<<"  "<<dfn[i]<<"   "<<low[i]<<endl;
    for(int i = 1; i <= n; ++i)
        if(cut[i])
            cout << i << ' ';
    cout << "are cut-vertexes" << '\n';
    return 0;
}

裸的求割点的应用、板题、poj1144
注意这个题输入的时候有点奇怪,在输入这个点所有的相连接的边的时候用一个getchar() != '\n'来判断是否到头,然后再输入和这个点相邻的所有其他点。
其他的直接套求割点的板子就好

//
//  main.cpp
//  无向图求割点_poj1144
//
//  Created by 陈冉飞 on 2019/8/20.
//  Copyright © 2019 陈冉飞. All rights reserved.
//

//#include<bits/stdc++.h>
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 100086;
struct node {
    int y, net;
}e[maxn << 1];
int lin[maxn], len = 0;
int n, root, num = 0,p,q,ans;
int dfn[maxn], low[maxn];
bool cut[maxn];
#include <cstring>
#define cl(a,b) memset(a,b,sizeof(a))

inline void insert(int xx, int yy) {
    e[++len].y = yy;
    e[len].net = lin[xx];
    lin[xx] = len;
}

void tarjan(int x) {
    dfn[x] = low[x] = ++num;
    int flag = 0;
    for(int i = lin[x]; i; i = e[i].net) {
        int to = e[i].y;
        if(!dfn[to]) {
            tarjan(to);
            low[x] = min(low[x], low[to]);
            if(low[to] >= dfn[x]) {
                flag++;
                if(x != root || flag > 1) cut[x] = true;
            }
        }
        else low[x] = min(low[x], dfn[to]);
    }
}

int main() {
    while (scanf("%d",&n) && n) {
        cl(lin,0);cl(dfn, 0);cl(low, 0);cl(cut, 0);ans = 0;num = 0;len = 0;
        while (scanf("%d",&p) && p) {
            while (getchar() != '\n') {
                scanf("%d",&q);
                insert(p, q);insert(q, p);
            }
        }
    
    for(int i = 1; i <= n; ++i)
        if(!dfn[i]) {
            root = i;
            tarjan(i);
        }
    for(int i = 1; i <= n; ++i)
        if(cut[i]) ans++;
        cout<<ans<<endl;
    }
    return 0;
}

  • 割边
    dfn[u] < low[v]**时(u,v)为割边,分析见割点。
在这里插入代码片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值