【tarjan双连通求割点&连通分量】POJ 1523

/*
构建一棵dfs树,序列dfn[i]为深度优先数,表示dfs时访问i节点的序号,low[i]表示从i节点出发能访问到的最小的深度优先数。

当且仅当节点u满足如下两个条件之一时,u为割点:
1.u为dfs树的根,且u至少有两个子节点。
2.u不是dfs树的根,至少存在一个节点v是u的子节点,且low[v]>=dfn[u]。
若u为割点,记subnets[u]为u的子节点数,则去掉u后,图被分成subnets[u]+1个部分(每个子节点的部分和u的祖先的部分),若u为dfs树的根,则分成subnets[u]个部分(根节点没有祖先)。
以上全部算法均在dfs过程中完成。
*/
#define N 1010
struct edge{
    int v;
    int next;
}e[N*5];
int ecnt;
int head[N];
void init(){
    ecnt = 0;
    memset(head,-1,sizeof(head));
}
void add(int u,int v){
    e[ecnt].v = v;
    e[ecnt].next = head[u];
    head[u] = ecnt++;
    e[ecnt].v = u;
    e[ecnt].next = head[v];
    head[v] = ecnt++;
}
int low[N],dfn[N];
int cut[N];
int n,m;
int t;
int ans;
int sub[N];
//tarjan求无向图双连通图
void tarjan(int u,int fa){
    low[u] = dfn[u] = ++t;
    int i;
    for(i=head[u];i!=-1;i=e[i].next){
        int v = e[i].v;
        if(v == fa)continue;
        if(!dfn[v]){
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
            if(dfn[u]<=low[v]){//u是割点
                if(u!=1)sub[u]++;//假定1为根结点
                else ans++;
            }
        } else low[u] = min(low[u],dfn[v]);//返祖边
    }
}

int main(){
    int a,b;
    int ca=1;
    while(1){
        scanf("%d",&a);
        if(!a)break;
        init();
        memset(dfn,0,sizeof(dfn));
        scanf("%d",&b);
        add(a,b);
        int node = 0;
        node = max(a,b);
        while(scanf("%d",&a) && a){
            scanf("%d",&b);
            add(a,b);
            node = max(node,max(a,b));
        }
        int i,j;
        ans=t=0;
        memset(sub,0,sizeof(sub));
        tarjan(1,1);//假定1为根结点
        if(ca>1)puts("");
        printf("Network #%d\n",ca++);cout<<ans<<endl;
        if(ans>1)sub[1] = ans-1;//根至少有两个儿子才算是割点
        bool ok=0;
        for(i=1;i<=node;i++){
            if(sub[i]){
                ok  = 1;
                printf("  SPF node %d leaves %d subnets\n",i,sub[i]+1);
            }
        }
        if(!ok)puts("  No SPF nodes");
    }
    return 0;
}



















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值