无向图的双连通分量

双连通分量

点_双连通分量 BCC
  对于一个连通图,如果任意两点至少存在两条“点不重复”的路径,则说图是点双连通的(即任意两条边都在一个简单环中),点双连通的极大子图称为点_双连通分量。 通常来说,如果要求任意两条边在同一个简单环中,那么就是求点-双连通
  易知每条边属于一个连通分量,且连通分量之间最多有一个公共点,且一定是割顶。

#include 
#include
#include
using namespace std;
const int MAXN=10010;
vector graph[MAXN];
int dfn[MAXN];//第一次访问的时间戳
int clocks;//时间戳
int isCut[MAXN];//标记节点是否为割顶
struct Edge
{
    int u,v;
    Edge(){}
    Edge(int u,int v):u(u),v(v){}
};
vector edge;//DFS访问过的边
vector bcc[MAXN];//点_双连通分量
int bccno[MAXN];//节点属于的点_双连通分量的编号
int bcc_cnt;//点_双连通分量的数目
int DFS(int u,int fa)
{
    int lowu=dfn[u]=++clocks;
    int child=0;
    for(int i=0;i=dfn[u])//找到了一个子树满足割顶的条件
            {
                isCut[u]=1;
                bcc_cnt++;
                bcc[bcc_cnt].clear();
                while(true)//保存bcc信息
                {
                    Edge ee=edge.back();
                    edge.pop_back();
                    //bccno[ee.u]!=bcc_cnt是为了防止重复加点
                    if(bccno[ee.u]!=bcc_cnt) { bcc[bcc_cnt].push_back(ee.u); bccno[ee.u]=bcc_cnt;}
                    if(bccno[ee.v]!=bcc_cnt) {bcc[bcc_cnt].push_back(ee.v);bccno[ee.v]=bcc_cnt;}
                    if(ee.u==u&&ee.v==v) break;
                }
            }
        }
        else if(dfn[v]

边_双连通分量 EBC
  边双连通分量是指任意两点存在至少两条"边不重复"的路径的图,还可以理解为每条边都至少在一个简单环,即每条边都不是桥。如果要求某条边被删除了,但是图G能够在删除任意一条边后,仍然是连通的,当且仅当图G至少为双连通的。
  对于边
双连通分量的求解简单多了,我们先找出所有的桥,并将其做上标记。然后在利用dfs遍历连通分量,只需在遍历时不访问桥即可。

  #include
  #include
  #include
  using namespace std;
  const int MAXN=1010;
  const int MAXE=2010;
  struct Node
  {
      int to,next;
      bool cut;//边是否为桥
  };
  Node edge[MAXE];
  int head[MAXN];
  int cnt;
  int dfn[MAXN];
  int low[MAXN];
  int clocks;
  int belong[MAXN];//点属于哪个边连通分量
  int blocks;//连通分量数
  void addEdge(int u,int v)
  {
      edge[cnt].to=v;
      edge[cnt].next=head[u];
      edge[cnt].cut=false;
      head[u]=cnt++;
  }
  //e是为了去重,e是边在数组的位置
  //另一种去重为DFS(u,fa),v!=fa,但是有重边时可能会判断错误
  //比如没重边时,假设(a,b)是桥,但是如果(a,b)有重边,那么(a,b)就不是桥了
  void DFS(int u,int e)//求出所有的桥
  {
    low[u]=dfn[u]=++clocks;
    for(int i=head[u];i!=-1;i=edge[i].next)
    {
        int v=edge[i].to;
        if(i==(e^1)) continue;//这里只会去重该边的反边,不会去它的重边
        if(dfn[v]==0)
        {
            DFS(v,i);
            low[u]=min(low[u],low[v]);
            if(low[v]>dfn[u])
            {
                edge[i].cut=1;
                edge[i^1].cut=1;
            }
        }
        else if(dfn[v]

还有一种办法是栈模拟,直接在DFS里面求出边连通分量,基于一个这样的事实,桥的端点的dfn[u]=low[u]
简要证明以下:
low[u]表示u及其后代能连回的最早的祖先的dfn值,所以low[u]<=dfn[u]总是成立。假设对于桥的一个端点u,low[u]!=dfn[u],即low[u]

     #include
     #include
     #include
     using namespace std;
     const int MAXN=1010;
     const int MAXE=2010;
     struct Node
     {
         int to,next;
         bool cut;
     };
     Node edge[MAXE];
     int head[MAXN];
     int low[MAXN],dfn[MAXN],onStack[MAXN];
     int cnt,clocks;
     stack sta;
     int belong[MAXN];
     int blocks;
     void addEdge(int u,int v)
     {
         edge[cnt].to=v;
         edge[cnt].next=head[u];
         edge[cnt].cut=false;
         head[u]=cnt++;
     }
     void DFS(int u,int e)
     {
        low[u]=dfn[u]=++clocks;
        onStack[u]=1;
        sta.push(u);
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if(e==(i^1)) continue;
            if(dfn[v]==0)
            {
                DFS(v,i);
                low[u]=min(low[u],low[v]);
                if(low[v]>dfn[u])
                {
                    edge[i].cut=true;
                    edge[i^1].cut=true;
                }
            }
            else if(onStack[v]&&dfn[v]

你可能感兴趣的:(无向图的双连通分量)