【笔记】【图论】强连通、割点和桥

(1)一些定义:

  1. 割点:若一个点删除后(与之相连的边统统去掉),无向图不再连通,那么此点称为割点。
  2. 桥:若一条边断去后,无向图不再连通,那么此边称为桥。桥有一个很好的性质,就是DFS一个无向图,那么这个过程必定要经过桥
  3. 1-连通图:具有割点的连通图。
  4. 2-连通图:至少要去掉两个顶点及其相关联的边,才能使该图的连通分支增加。
  5. 块:没有割点的无向图,称为2-连通分支,也称作块。
  6. 强连通:一个环,或者多个环相套。则一个强连通中的任何点都可以互相到达

 

(2)求强连通块——算法思路

随手一个简单强连通,

1->2->3->1

思考程序如何才能让计算机模拟人的眼睛,判断出这是一个强连通,

因为是边的相连导致强连通块,所以肯定要按照边的连通情况,遍历图,

这里显然dfs比bfs更合适,所以我们按照1->2->3的顺序遍历

接下来到了3->1的边,这个边肯定有特征,让计算机确定这是一个强连通

我们发现:

1)1号点已经遍历过

2)这是一条回边

这样我们可以设一个dfn的量,保存每个点遍历时的时间前后

 

现在我们看我们找强连通是为了什么:

1)计算有几个强连通

2)把环去掉,叫做tarjan缩点

 

那么对于目的1)

我们的计数肯定是记每个强连通的结束的点,

就是最先遍历的那个点,

那这个点的特征(和别的点的区别)在哪呢?

答案是他没有回边,就是他的子节点遍历中,遇不到一个点的dfn比他早

那么我们可以找一个记录的量,先叫做t,

这个t应该是记录每个点子节点中dfn的最小值

(因为最高点的dfn最小,其他的都比他的dfn大,那么我们要记录的就是dfn的下限)

那么我们就把t改成low,好听,不易重名

 

思路就出来了,找没有遍历过的点遍历,

在3节点时,找能到达的点集合,如果有一个遍历过的点,那他的dfn一定小,

我们就更新3节点

为了让2,这种中间点的dfn也发生改变,那么在2->3之后,也要更新2的dfn

所以我们的结构就出来了

但是那些遍历过的点,可能是别的强连通分量中的,所以一个stack,每次找完弹出一个强连通

 

ps:

tarjan中单个点也算一个强连通

#include<bits/stdc++.h>
using namespace std;

int n;
const int N=1e5+10;
vector <int > son[N];

int dfn[N],low[N];
int blocks,block_id;//blocks是连通块的数量 

bool insta[N];
stack <int > sta;

void tarjian(int x)
{
    dfn[x]=low[x]=++tot; //进入先记录
    sta.push(x),insta[x]=true;
    
    int sz = son[x].size();
    for(int i=0;i<sz;i++)
    {
        int nx=son[x][i];
        if(!dfn[nx])
        {
            tarjian(nx);
            low[x]=min(low[x],low[nx]);
        }
        else if(insta[nx]) //如果这个点已经遍历过,且在sta中 => 是回边 => 会影响low 
        { 
            low[x]=min(low[x],low[nx]); 
        }
        //比如G:12341 加上边4->5 5->6 4->6
        //按照函数,操作4->6的时候,6访问过,但是不在栈中 
        //其实还是不清楚,如果没有判断insta,会出现什么情况 
    }
    
    if(low[x] == dfn[x] ) //说明x是此连通块的最高点,从栈中弹出所有 
    {
        //如果要统计点sta.top()归属哪个连通块 
        belong[x]=++block_id;
        
        while(sta.top()!=x) //弹出栈中所有点    
        {
            //如果要统计点sta.top()归属哪个连通块 
            belong[sta.top()]=block_id;
             
            insta[sta.top()]=false;
            sta.pop(); 
        }
        insta[x]=false;
        sta.pop();
        
        blocks++;
    }
}

int main()
{
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjian(i);
    
    
    return 0;
} 

 

 

对于目的2)

看题!

poj2186 Popular Cows

告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜。

缩点思路:将一个强连通分量->变成超级源点

这个超级源点,用fa表示,或者用新的sum=n,++sum也行

主要区别:

    if(dfn[u]==low[u])
    {
        color[u]=++sum;
        vis[u]=0;
        while(stack[top]!=u)
        {
            color[stack[top]]=sum;
            vis[stack[top--]]=0;
        }
        top--;
    }

注意,缩点之后的边要区分强连通内,和强连通外,

入度出度统计都要重新注意

posted @ 2019-08-26 19:21  心若笺诗  阅读(288)  评论(0编辑  收藏  举报