连通图/割点、割边

先了解一下每个名词的含义

无向图:没有方向的图
无向连通图:图中任意两节点可互相到达的无向图
无向非连通图:不是无向连通图
无相连通分量:无向非连通图中的最大连通子图
割点:删掉这个点就不连通
割边:删掉这个边就不连通
强联通有向图:任意两点可以到达的有向图
弱连通有向图:如果忽略边的方向是一个无向连通图
强联通分量(SCC):非强连通图的极大连通子图

对于求无向图中的割点和割边

为什么有割点和非割点之分,那就是由于无向图连通有环,环中的点自然不是割点,如果无向连通图要是树的话,那么所有点就都是割点了

对于Tarjan算法,则是选择一个为根节点进行dfs搜索,用dfn数组存储每个节点出现时间(时间从1开始,0代表没搜过)

如果该图中有环那么该环中一定有first(最早出现的节点)和last(最晚出现的节点),而且last与fisrt有边能直接相连,当last搜寻下一个节点时便会搜到first

这时需要一个low数组存储每个点能搜索到的最早出现的时间,只有对于环中last搜到fisrt时,low数组能变小,而且会让从fisrt->last这条树链上的所有点i,使得low[i]=min(low[i],dfn[first])(代码中应该是low[u]=min(low[u],low[v]))

搜完所有点以后

若求割点每个点可以分为根节点和不是根节点

对于根节点,若有两个子节点那么该点即是割点(对于如何判断有几个子节点,在搜索时用一个fa[]数组存好由谁转移过来的,最后以判断有几个是由根节点转换的即可)

对于不是根节点i,若low[i]>=dfn[fa[i]]则该点父亲是割点(因为该点不通过该点与父亲的边最多只能搜到父亲节,那么删掉父亲节点,该点则不能搜到父亲以上节点)

若求割边,只需要搜每个不是根节点的点i,若low[i]>dfn[fa[i]]那么该点与父亲连的边为割边(因为若该点所能搜到的最早出现的点也比父亲出现的晚,说明该点不能搜到父亲上面的点)

代码如下

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
using namespace std;
int dfn[1005],low[1005],fa[1005];
bool vis[1005];
vector<int>G[1005];
int cnt;
void tarjan(int u)
{
    dfn[u]=low[u]=++cnt;
    int i,j;
    for(i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(dfn[v]==0)
        {
            fa[v]=u;
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
            low[u]=min(low[u],dfn[v]);
    }
}
int gedian(int n)
{
    int num=0;
    memset(vis,false,sizeof(vis));
    int i,j,fu;
    for(i=2; i<=n; i++)
    {
        fu=fa[i];
        if(fu==1)
            num++;
        else
        {
            if(low[i]>=dfn[fu])
                vis[fu]=true;
        }
    }
    if(num>=2)
        vis[1]=true;
    int sum=0;
    for(i=1; i<=n; i++)
    {
        if(vis[i])
            sum++;
    }
    return sum;
}
int gebian(int n)
{
    int i,j,sum=0,fu;
    for(i=1;i<=n;i++)
    {
        fu=fa[i];
        if(fu>0&&low[i]>dfn[fu])
            sum++;
    }
    return sum;
}
int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        int i,j;
        for(i=1; i<=n; i++)
            G[i].clear();
        int x,y;
        for(i=0; i<m; i++)
        {
            scanf("%d %d",&x,&y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        memset(dfn,0,sizeof(dfn));
        cnt=0;
        memset(fa,0,sizeof(fa));
        tarjan(1);

    }
}
跑一遍taijan以后有向图变成一棵树,对于原图环中一定被舍掉,但恰恰由于这条边改变了low值,因为树都是从上指向下,只有这条边往上指了,使得
该边下边点low值可以变成上边点dfn值
求割点
对于根如果有两棵子树则该点是割点
否则若该点u存在一个子节点v使得 low[v]>=dfn[u]则u为割点
求割边
对于某条边的子节点v父节点u存在 low[v]>dfn[u]则该边为割边

猜你喜欢

转载自www.cnblogs.com/noncontradiction/p/8950549.html