图的割点学习+模板题【Tarjan】

什么叫做割点?

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

image      图中c就是一个割点,剩下{A,B}和{C,D}两个连通分量

实现原理:

在学习割点的基础上应该了解什么是连通分量和Tanjan的实现过程。
求割点的过程主要是在Tarjan模板中加了一些判断。
Tarjan的过程主要就是DFS,在DFS树中,我们发现有两种点可以成为割点:
①对于根节点,如果它有两个或两个以上的儿子,则该根节点为割点(注意儿子数量均为DFS树中的儿子个数)
②对于非叶子结点s,如果它的儿子结点e不通过s就可以访问s的祖先结点的话,那么s点就不是割点。反之,只要存在一个儿子结点e必须通过s来访问s的祖先结点的话,那么s就是一个割点。

判断条件:

对于跟结点我们上面提过了,只需要判断DFS树中根节点的儿子数量。
对于非叶子结点:
我们用 dfn[u] 记录节点u在DFS过程中被遍历到的次序号, low[u] 记录节点u或u的子树通过非父子边追溯到最早的祖先节点(即DFS次序号最小),那么 low[u] 的计算过程如下:

模板题:POJ1523

题意:给你一个无向图,要求你输出割点的标号和图连通分量的个数

思路:判断割点就用模板,问题是图联通分量的个数是多少?
①如果根节点是割点,那么图的连通分量个数就是DFS树中根节点的儿子数量
②如果非叶子结点是割点,那么图的连通分量的个数就是DFS树中满足条件 low[v]>=dfn[u] (v是u的儿子)的个数 +1

附上代码(含解析)
 

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<new>
#include<vector>
#define MT(a,b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai=acos(-1.0);
const double E=2.718281828459;
const int mod=1e9+7;
const int INF=0x3f3f3f3f;

struct node
{
    int e;
    int p;
}load[1000005];
int head[1005],sign;

void add_edge(int s,int e)
{
    load[++sign]=node{e,head[s]};
    head[s]=sign;
}

int dfn[1005],low[1005],time;
int cut[1005];
///记录删除该点可以分成几个连通分量

void tarjan(int s,int pre)
{
    dfn[s]=low[s]=++time;
    int son=0,cnt=1;
    /*
        cnt用于非根节点
        cnt初始化为1,因为如果s为割点。
        去掉s留下的连通分量个数=儿子中满足(low[e]>=dfn[s])的个数+1
    */
    ///son用于记录儿子个数,用于根结点的
    for(int i=head[s];i!=-1;i=load[i].p)
    {
        int e=load[i].e;
        if(e!=pre)
        {
            if(!dfn[e])///如果没有访问过
            {
                son++;  ///儿子的个数+1
                tarjan(e,s);
                low[s]=min(low[s],low[e]);
                ///如果当前不为根节点
                ///并且e必须通过s访问s的祖先
                if(pre!=-1&&low[e]>=dfn[s])
                    cut[s]=++cnt;
            }
            else
                low[s]=min(low[s],dfn[e]);
        }
    }
    /*
        如果当前s为根节点,且儿子个数>=2,
        那么s为割点,去掉s后的连通分量数量
        就等于儿子的个数
    */
    if(pre==-1&&son>=2)
        cut[s]=son;
}

void init()
{
    time=sign=0;
    memset(head,-1,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(cut,0,sizeof(cut));
    ///去掉第i个点,剩余连通分量的个数
    ///cnt[i]只能=0或者>=2
    ///=0即该点不是割点
}

int main()
{
    int n,s,e,cas=0;
    while(scanf("%d",&s)!=EOF,s)
    {
        init(); ///初始化
        scanf("%d",&e);
        add_edge(s,e);
        add_edge(e,s);
        n=max(s,e);
        while(scanf("%d",&s)!=EOF,s)
        {
            scanf("%d",&e);
            add_edge(s,e);
            add_edge(e,s);
            n=max(e,s);
        }
        tarjan(1,-1);
        if(++cas!=1)
            printf("\n");
        printf("Network #%d\n",cas);
        int flag=1;
        for(int i=1;i<=n;i++)
        {
            if(cut[i])
            {
                printf("  SPF node %d leaves %d subnets\n",i,cut[i]);
                flag=0;
            }
        }
        if(flag)
            printf("  No SPF nodes\n");
    }
    return 0;
}
/*
 1 2
 1 3
 1 4
 1 5
 2 3
 0

答案
Network #1
  SPF node 1 leaves 3 subnets
*/

参考博客:https://www.cnblogs.com/nullzx/p/7968110.html
                  http://www.cnblogs.com/en-heng/p/4002658.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值