POJ

题目链接:http://poj.org/problem?id=1144

题目意思:求一个图的割点个数

割点:在一个无向图中,如果删除一个顶点以及所有相关联的边以后,图的连通分量增多,就称这个点为割点。

 

1、如果u为树根,那么u有2棵以上子树,则是割点。这种情况容易处理

2、如果u不为树根,那么(u,v)为树枝边,当Low[v]>=DFN[u]时。解释如下:

选定一个根节点,从该根节点开始遍历整个图(使用DFS),树上的边一定都是图上的边,称为树边,而图上其余没遍历的边则为非树边(回边)。

如果一个点不能通过非树边而回到比他树上的父亲的dfs序更小的点,那么如果把它树上的父结点删掉,它就不能通过其他方法与图的其他部分联通,即其父节点为割点。

void tarjan(int u,int fa)//搜索点u,fa为u的根节点。 
{
    low[u]=dfn[u]=++tot;
    int son=0;//记录子树个数 
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(!dfn[v])
        {
            tarjan(v,fa);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=fa)//情况二,v通过回边返回不了比u更早的点。 
                flag[u]=1;
            if(u==fa)//u是根节点,v这是u的一个子树 
                son++;
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
    if(son>=2&&u==fa)//情况一,根节点子树>=2 
        flag[u]=1;
}
View Code

dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。

 

但这里也出现一个问题:怎么计算low[u]。

 

假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。

有一条边(u, v),如果v未访问过,继续DFS,即通过回边搜索,回溯时 low[u]=min(low[u], low[v]);

如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])

转载自:https://www.cnblogs.com/collectionne/p/6847240.html

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define inf 0x3f3f3f3f
using namespace std;
typedef long long ll;
const int maxn=150;
struct node{
    int next,to;
}edge[maxn*maxn];

int head[maxn],flag[maxn],low[maxn],dfn[maxn];
int n,m,ans,cnt,tot;

void init()//初始化 
{
    memset(head,-1,sizeof(head));
    memset(flag,0,sizeof(flag));
    memset(low,0,sizeof(low));
    memset(dfn,0,sizeof(dfn));
    ans=cnt=tot=0;
}

void add(int u,int v)//连接两点 
{
    edge[cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}

void tarjan(int u,int fa)//搜索点u,fa为u的根节点。 
{
    low[u]=dfn[u]=++tot;
    int son=0;//记录子树个数 
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v,fa);
            low[u]=min(low[u],low[v]);
            if(low[v]>=dfn[u]&&u!=fa)//情况二,v通过回边返回不了比u更早的点。 
                flag[u]=1;
            if(u==fa)//u是根节点,v这是u的一个子树 
                son++;
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
    if(son>=2&&u==fa)//情况一,根节点子树>=2 
        flag[u]=1;
}

int main()
{
    while(~scanf("%d",&n)&&n)
    {
        init();
        while(1)
        {
            int v;
            scanf("%d",&m);
            if(!m)    break;
            while(1)
            {
                scanf("%d",&v);
                add(m,v);
                add(v,m);
                if(getchar()=='
')
                    break;
            }
        }
        tarjan(1,1);
        for(int i=1;i<=n;i++)
            if(flag[i]>0)
                ans++;
        cout<<ans<<endl;
    }
    return 0;
}
原文地址:https://www.cnblogs.com/xiongtao/p/9970426.html