poj1144( 求割点数)

<pre name="code" class="cpp">别人写的对割点的详细介绍
无向图求点割集算法
 出处:http://blog.csdn.net/xinghongduo/article/details/6202646

黑书上给出了关于求点割集的算法,但是比较模糊,我查阅了网络上的相关资料,理解了求点割集的过程,写出如下求点割集的代码,并写了一些简单的证明.
 
割点集的定义:如果在连通图G中去掉某一点后图不连通,那么这个点即为G的割点,所有割点的集合即为点割集。
 
求点割集的方法:利用tarjan算法的思想,用数组dfn[v]存储DFS遍历到点v的时间,数组low[v]存储点v能追溯到最早的祖先节点。
如果对于点v来说有如下结论:
 
 
1.如果点v是DFS序列的根节点,则如果v有一个以上的孩子,则v是一个割点。
2.如果v不是DFS序列根节点,并且点v的任意后继u能追溯到最早的祖先节点low[u]>=dfn[v],则v是一个割点。
 
 
证明1:
  
       假设DFS遍历的第一个节点v不是割点,那么则有low[v]=dfn[v]=1,继续对v的孩子节点u遍历,必然有low[u]>=dfn[v],按照第二条性质,则v是割点,但我们已经假设v不是割点。这是由于v是DFS遍历的起始节点,在遍历序列中v没有祖先节点,v的所有后继节点能追溯到最早的祖先节点最多也就是v了,不可能比v再早了,因此必须把DFS遍历的第一个节点v单独考虑,那么怎么判断v是不是割点呢?

      例如上图,设v是DFS序列访问的第一个节点,对v的孩子节点u和u的所有孩子节点进行DFS遍历并标记为已经访问后,如果v的另一个孩子节点k没有被标记为已经访问,那么u和k之间一定不存在边,也就是说u和k之间的连通必然需要点v,因此如果v是DFS遍历的第一个节点,对v是否为割点的判断方法是:看v是不是有多个孩子,如果有则v是割点。
证明2:
      如果v不是DFS遍历的第一个节点,那么对于v的所有后继节点来说,如果v不是割点(也就是如果删掉点v,剩下的图还是连通图),那么v的后继节点必然能追溯到DFS遍历序列中v的祖先节点,也就是v的后继节点中存在到达DFS序列中v的祖先的路径,因此当DFS回溯到v节点时对于v的所有后继节点u来说,都有low[u]<dfn[v]。
      如果v是一个割点,对所有v的后继节点u进行DFS后,必然有low[u]>=dfn[v],这是因为,当遍历v并将其锁定后,到达v的祖先节点的路径已经被封死,v的后继节点必然不可能访问到v的祖先节点,因此,必然有low[u]>=dfn[v]。
 

 


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;

const int M = 150;

int dfn[M], low[M],vist[M], flag[M];
vector<int>que[M];
int n, index, root;
int ans;

void init() {

    memset(dfn, 0, sizeof(dfn));
    memset(vist, 0, sizeof(vist));
    memset(flag, 0, sizeof(flag));
    for(int i = 0; i <= n; i++)
        que[i].clear();
    index = 1;
    root = 1;
}

void Tarjian(int u) {

    dfn[u] = low[u] = index++;
    int cnt = 0;
    for(int i = 0; i < (int)que[u].size(); i++){

        int v = que[u][i];
        if(!dfn[v]){

            cnt++;
            Tarjian(v);
             low[u] = min(low[u], low[v]);
            if(u == root&&cnt == 2) flag[u] = 1;  //此点是根节点,且是割点。
            if(u != root && dfn[u] <= low[v]) flag[u] = 1;
        }else if(v != u)
            low[u] = min(low[u], dfn[v]);

    }
}

int main()
{
   int a, b;
    while(scanf("%d", &n) != EOF){

        if(n == 0)
            break;
            init();
        while(scanf("%d", &a)&&a){

            while(getchar() != '\n'){

                scanf("%d", &b);
           // printf("a = %d  b == %d\n", a, b);
                que[a].push_back(b);
                que[b].push_back(a);
            }
        }
        Tarjian(1);
        ans = 0;
        for(int i = 1; i <= n; i++)
            if(flag[i])
            ans++;
        printf("%d\n", ans);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值