POJ1144 Network【Targin求割点】

Tarjan算法

可以使用Tarjan算法求割点(注意,还有一个求连通分量的算法也叫Tarjan算法,与此算法类似)。(Tarjan,全名Robert Tarjan,美国计算机科学家。)
首先选定一个根节点,从该根节点开始遍历整个图(使用DFS)。

对于根节点,判断是不是割点很简单——计算其子树数量,如果有2棵即以上的子树,就是割点。因为如果去掉这个点,这两棵子树就不能互相到达。

对于非根节点,判断是不是割点就有些麻烦了。我们维护两个数组dfn[]和low[],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,DFS完之后,low[u]=min(low[u], low[v]);

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

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

int head[11111];
int dfn[11111];
int low[11111];
int ans[11111];
int cont,cnt,root;

struct node
{
    int to;
    int next;
} edge[21111];

int min(int a, int b)
{
    if (a<b) return a;
    else return b;
}

void init()
{
    cont=0;
    cnt=0;//记录顶点第几个被(首次)访问
    root=1;
    memset(head,-1,sizeof(head));
    memset(ans,0,sizeof(ans));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
}
void add(int from,int to)
{
    edge[cont].to=to;
    edge[cont].next=head[from];
    head[from]=cont++;
}

void Targin(int u,int from)
{
    cnt++;
    dfn[u]=low[u]=cnt;
    int son=0;
    for(int k=head[u]; k!=-1; k=edge[k].next)
    {
        int v=edge[k].to;
        if(dfn[v]==0)
        {
            Targin(v,u);
            son++;
            low[u]=min(low[u],low[v]);
            if((dfn[u]<=low[v]&&u!=1)||(u==1&&son>1))
                ans[u]=1;
        }
        if(v!=from)
        {
            low[u]=min(low[u],dfn[v]);
        }
    }
}


int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        if(n==0) break;
        init();
        int a,b;
        while(~scanf("%d",&a))
        {
            if(a==0)break;
            while(getchar()!='\n')
            {
                scanf("%d",&b);
                add(a,b);
                add(b,a);
            }
        }
        Targin(1,1);//自己是自己的父亲
        int sum=0;
        for(int i=1; i<=n; i++)
        {
            if(ans[i]==1)
            {
                sum++;
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

https://www.cnblogs.com/collectionne/p/6847240.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值