POJ1144--Network(Tarjan求割点)

题目大意:有一个电话公司,他们的电话可以直连,也可以通过电话交换机与其他电话相连。当供电不足时,有的电话或者电话交换机就不能用了,会导致公司里剩余的电话不能相互通话,我们把这样的电话或者电话交换机称为关节点。


分析:抽象出来的模型就是,一个无向连通图求割点。

无向连通图求割点的Tarjan算法,需要下面3个数组。

dfn[i]表示访问顺序,也称开始时间。

low[i]表示i或者i的子树中能够通过非父子边(父子边就是搜索树上的边)追溯到的最早的节点的DFS开始时间。

fa[i]记录i的父节点。


判断一个顶点是否为割点,只需满足下面任意一个条件即可:

(1) u为树根,且u有多于一个子树。 

(2) u不为树根,且存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得dfn(u)<=low(v)。


#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 111;

int n;
vector<int> g[maxn];
int dfn[maxn], low[maxn];
bool cut[maxn];     //点v是否为割点
int fa[maxn];
int index;  //DFS时间戳

void tarjan(int u, int father) {
    dfn[u] = low[u] = ++index;
    fa[u] = father;
    int len = g[u].size();
    for(int i = 0; i < len; i++) {
        int v = g[u][i];
        if(dfn[v] == 0) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(v != father)
            low[u] = min(low[u], dfn[v]);
    }
}

int main() {
    while(scanf("%d", &n) && n) {
        for(int i = 1; i <= n; i++)
            g[i].clear();
        int u, u0;
        while(scanf("%d", &u) && u) {
            u0 = u;
            int v;
            while(getchar() != '\n') {
                scanf("%d", &v);
                g[u].push_back(v);
                g[v].push_back(u);
            }
        }
        memset(dfn, 0, sizeof(dfn));
        memset(cut, 0, sizeof(cut));
        memset(fa, 0, sizeof(fa));
        memset(low, 0, sizeof(low));
        int son = 0;
        index = 0;
        tarjan(u0, 0);
        for(int i = 1; i <= n; i++) {
            if(i == u0) continue;
            int v = fa[i];
            if(v == u0) son++;      //记录根的子树的个数,即割点条件(1)
            else if(dfn[v] <= low[i]) cut[v] = 1;   //割点条件(2)
        }
        if(son > 1) cut[u0] = 1;
        int ans = 0;
        for(int i = 1; i <= n; i++)
            if(cut[i]) ans++;
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(POJ1144--Network(Tarjan求割点))