【图论算法及模版】割点,割边,强联通分量,点双联通分量,边双联通分量

将学的基础知识总结下 Orz LRJ

因为都是街货,为了防止大牛D,所以仅结合资料作简单总结


联通分量u -> v, v -> u,相互可达的节点称为连通分量

求联通分量个数只需要DFS一遍即可,也可用并查集去判联通块


割点

无向图G中,删除某点u后,若联通分量数目增加,则u为此图G中的一个割点。这意味着若在连通图中删除割点,则图变得不连通。

DFS整个图,利用时间戳打下标记,可以得到定理:非根节点u是图G的割点当且仅当u存在一个子节点v,使得v及其后代都没有反向边连向u的祖先(不包括u)。DFS森林的性质对于此定理的正确性而言极其重要!若令low[u]为u及其后代所能连回的最早的祖先的dfn值(时间戳),定理又可表述为:非根节点u是图G的割点当且仅当u存在一个子节点v,使得low[v]>=dfn[u]

我的模板(特判相对比较优美)

int dfs(int u, int father)
{
   int lowu = dfn[u] = ++dfstime, lowv, child = 0;
   for (int i = 0, v; i < (int) g[u].size(); ++i)
   {
      if (!dfn[v = g[u][i]])
      {
         ++child, lowu = Min(lowu, lowv = dfs(v, u));
         if (lowv >= dfn[u]) ic[u] = 1;
      }
      else if (dfn[v] < dfn[u] && v != father)
         lowu = Min(lowu, dfn[v]);
   }
   if (father < 0 && child == 1) ic[u] = 0;
   return dfn[u] = lowu;
}

int main()
{
   for (int u = 1; u <= n; ++u)
      if (!dfn[u]) dfs(u, -1);
   for (int i = 1; i <= n; ++i)
      if (ic[i]) ++icnum;
   printf("%d\n", icnum);//icnum为割点个数
   return 0;
}


割边

DFS森林的基础上,若v的后代只能连回v自己,(即low[v]>dfn[u]),那么(u,v)其实就是割边,即删除此边则图G联通分量数目增加,连通图变得不连通。


强连通分量

首先明确强连通分量是有向图中才有的说法,与无向图联通分量类似,且每一个强连通分量都是原图中的极大强连通子图。如果把每个强连通分量缩成一个点,可知缩点后的新图必然是有向无环图。

一个强联通分量C在DFS森林中第一个被发现的点若为x,则C中的其他点都是x的后代!也就是每个强联通分量都是树的一棵子树。可以将问题转化为判断一个点是否为一个强联通分量中最先被发现的点。

我的模板

void dfs(int u)
{
   dfn[u] = low[u] = ++dfstime; S.push(u);
   for (int i = 0, v; i < (int) g[u].size(); ++i)
   {
      if (!dfn[v = g[u][i]]) dfs(v), low[u] = Min(low[u], low[v]);
      else if (!toscc[v]) low[u] = Min(low[u], dfn[v]);
   }
   if (low[u] == dfn[u])
      for (++scctot; ; )
      {
         int x = S.top(); S.pop();
         toscc[x] = scctot;
         if (x == u) break;
      }
}

int main()
{
   for (int i = 1, u, v; i <= m; ++i)
      g[u = getint()].push_back(v = getint());
   for (int i = 1; i <= n; ++i)
      if (!dfn[i]) dfs(i);
   printf("%d\n", scctot);
   return 0;
}



点双连通分量

对于一个连通图,若任意两点至少存在两条“点不重复”的路径,则此连通图是点-双连通的(一般称双连通),意味着任意的两条边都是在同一个简单环上,即内部无割顶。点-双连通的极大子图叫做双连通分量。定理:不同双联通分量最多只有一个公共点,且它一定是割顶,任意割顶都是至少两个不同双连通分量的公共点。


边双连通分量

对于一个连通图,若任意两点至少存在两条“边不重复”的路径,则此连通图是边-双连通的。意味着只需要每条边都至少在一个简单环中,即所有边都不是桥。边-双连通的极大子图叫做边-双连通分量,除了桥不属于任何边-双连通分量之外,其他每条边恰好属于一个边-双连通分量,而且把所有桥删除之后,每个联通分量对应原图中的一个边-双连通分量。


推荐GJB的几篇文章

http://www.byvoid.com/blog/scc-tarjan/  有向图强联通分量法的Tarjan算法

http://www.byvoid.com/blog/biconnect/  图的割点、桥与双联通分支


有一个很重要的概念叫做缩点

联系上文易知共有三类缩点

一。 将每个强连通分量缩成一个点

二。 将每个点双连通分量缩成一个点

三。 将每个边双连通分量缩成一个点

缩点过程其实只要把DFS求上面三种东西的过程稍作修改即可

比如求强连通分量时,我的模板每个强连通分量缩成的点即为退栈时的SCCTOT

而toscc就是由原图中点指向新图中点的映射

若需要重新建图,则for每条边看两点是否在同一强连通分量中,否则就在新图中添加一条新边


PS:2-SAT问题可以利用缩点!


你可能感兴趣的:(图论)