《啊哈算法》——割点、割边、二分图

这篇文章我们简单的介绍求解图的割点、割边和二分图相关的概念。

割点:

对于含n个点、m条边的连通无向图G,如果去掉顶点vi(并同时去掉与之相连的边),使得G不再连通,那么称vi是一个割点。

通过其定义,我们不难判断某个点是否是割点,但是现在我们面临的问题是,如何给出一个图G,编码让计算机求解割点呢?

首先我们考虑这样一个问题,判定某个点的指标是什么。我们通过人脑来判断其是否是割点,其实是利用非常模糊的视觉效应,即“通过去掉该点观察图是否连通”即可,而如果想要通过计算机来判断,就需要非常量化的判断条件。

我们考虑从深度优先搜索的角度来找到这样一个判断条件,利用dfs遍历图,得到的生成子图本质上会得到一个生成树,我们拿出两个相邻的点vi、vj,vi是vj的父节点。我们回到深搜遍历的过程中,假设当前遍历到vj,如果我们从vj能够找到一条回到已经访问过的v1、v2...等节点,那么这表明去掉vi,将不会影响剩余图的连通性。

我们似乎发现了些什么,但是这种判定关系还是有些模糊。

我们借用这样一个概念——时间戳,即深度优先搜索的过程中,我们记录访问节点的顺序,我们用num[i]来表示节点vi的时间戳,即在深搜遍历过程中第几个访问vi节点。借用这个工具,我们考虑能不能将上述我们描述的关系用量化的表达式表示出来呢?好像还是有点捉襟见肘啊,我们不妨再设置一个数组low[i],用以表达vi不经过dfs的生成树的父节点所能够到达的时间戳最小的节点(好好理解,非常拗口),基于这个工具,我们能够看到上述的判断条件,可以用这样一个表达式简洁的概括:

low[i] < num[i]

那么现在我们首要的问题似乎变成了求解n个节点的low[]、num[]了。

首先,对于num[],也就是时间戳的记录,并不困难。而对于low[]数组的求解,就需要动一些脑筋了。我们模拟遍历过程,当前遍历到vi点,我们访问所有与vi连通的点vj,会出现如下两种情况。

1.vj访问过,被我们打上过时间戳,  那么我们此时需要更新low[i]了,即low[i] = min{num[j] | vj与vi连通}。

2.vj没有访问过,那么我们继续深搜遍历点的过程。

在遍历完成之后,也完成了num[]、low[]的求解,我们再利用深搜的回溯过程,完成判断即可。

这里需要注意的一点是,对于某个图的根节点,即dfs开始的那个点(记作v1)其实是不满足上文给出的判断式子的,需要我们特殊判断,记child是根节点的子树个数,则v1是个割点的必要条件是,child  = 2。

简单的参考代码如下。

#include<cstdio>
#include<algorithm>
using namespace std;

int n , m , e[9][9] , root;
int num[9] , low[9] , flag[9],index;
int min(int a , int b)
{
     return a < b ? a : b;
}
void dfs(int cur , int father)
{
    int child = 0 , i , j;

      index++;
      num[cur] = index;
      low[cur] = index;
      for(i = 1;i <= n;i++)
      {
            if(e[cur][i] == 1)
            {
                   if(num[i] == 0)  //第一种情况
                   {
                         child++;
                         dfs(i,cur);
                         low[cur] = min(low[cur] , low[i]);//回溯过程:判断割点

                         if(cur != root && low[i] >= num[cur])
                              flag[cur] = 1;
                         else if(cur == root && child == 2)
                              flag[cur] = 1;
                   }

                    else if(i != father) //第二种情况
                    {
                        low[cur] = min(low[cur] , num[i]);
                    }

            }
      }
}

int main()
{
     int i , j, x , y;
     scanf("%d %d",&n,&m);
     for(i = 1;i <= n;i++)
          for(j = 1;j <= n;j++)
             e[i][j] = 0;
     for(i = 1;i <= m;i++)
     {
          scanf("%d %d",&x,&y);
          e[x][y] = 1;
          e[y][x] = 1;
     }

     root = 1;
     dfs(1,root);

     for(i = 1;i <= n;i++)
     {
           if(flag[i] == 1)
              printf("%d ",i);
     }

     return 0;
}
时间: 2024-10-21 04:54:14

《啊哈算法》——割点、割边、二分图的相关文章

ZOJ Problem - 2588 Burning Bridges tarjan算法求割边

题意:求无向图的割边. 思路:tarjan算法求割边,访问到一个点,如果这个点的low值比它的dfn值大,它就是割边,直接ans++(之所以可以直接ans++,是因为他与割点不同,每条边只访问了一遍). 需要注意的就是此处有多重边,题目中要求输出确定的不能被删除的边,而多重边的保留不是可以确定的,所以多重边都是不可以被保留的,我们可以在邻接表做一个flag的标记,判断他是不是多重边. 注意建图的时候数组应该是m × 2,因为这里是无向边,当心RE! 注意输出的时候编号是必须要拍好序再输出. 还有

算法大讲堂之二分图

二分图大讲堂——彻底搞定最大匹配数(最小覆盖数).最大独立数.最小路径覆盖.带权最优匹配 文本内容框架: §1图论点.边集和二分图的相关概念和性质 §2二分图最大匹配求解 匈牙利算法.Hopcroft-Karp算法 §3二分图最小覆盖集和最大独立集的构造 §4二分图最小路径覆盖求解 §5二分图带权最优匹配求解 Kuhn-Munkers算法 §6小结 每章节都详细地讲解了问题介绍,算法原理和分析,算法流程,算法实现四部分内容,力求彻底解决问题. §1图论点.边集和二分图的相关概念和性质 点覆盖.最

算法讲解:二分图匹配

算法讲解:二分图匹配 二分图匹配,自然要先从定义入手,那么二分图是什么呢? 二分图: 二分图又称作二部图,是图论中的一种特殊模型. 设G=(V,E)是一个无向图,如果顶点V可分割为两个互不相交的子集(A,B),并且图中的每条边(i,j)所关联的两个顶点i和j分别属于这两个不同的顶点集(i in A,j in B),则称图G为一个二分图. 简单的说,一个图被分成了两部分,相同的部分没有边,那这个图就是二分图,二分图是特殊的图. 匹配: 给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两

求割点 割边 Tarjan

附上一般讲得不错的博客 https://blog.csdn.net/lw277232240/article/details/73251092 https://www.cnblogs.com/collectionne/p/6847240.html https://blog.csdn.net/zhn_666/article/details/77971619 然后附上模板题:              https://vjudge.net/problem/HihoCoder-1183 裸题,直接要你输

Tarjan算法与割点割边

目录 Tarjan算法与无向图的连通性 1:基础概念 2:Tarjan判断割点 3:Tarjan判断割边 Tarjan算法与无向图的连通性 1:基础概念 在说Tarjan算法求解无向图的连通性之前,先来说几个概念: <1. 时间戳:在图的深度优先遍历中,按照每一个结点第一次被访问到的时间顺序,依次给予N个结点1~N的整数边集,该标记就被计位"时间戳",计做 \(dfn[x]\). <2. 搜索树:任选一个结点深度优先遍历,每个点只访问一次.产生递归的边构成的树为搜索树. &

【学习整理】Tarjan:强连通分量+割点+割边

Tarjan求强连通分量 在一个有向图中,如果某两点间都有互相到达的路径,那么称中两个点强联通,如果任意两点都强联通,那么称这个图为强联通图:一个有向图的极大强联通子图称为强联通分量.   算法可以在 的时间内求出一个图的所有强联通分量. 表示进入结点 的时间 表示从 所能追溯到的栈中点的最早时间 如果某个点 已经在栈中则更新  否则对 进行回溯,并在回溯后更新  #include<iostream> #include<cstdlib> #include<cstdio>

匈牙利算法dfs模板 [二分图][二分图最大匹配]

最近学了二分图最大匹配,bfs模板却死活打不出来?我可能学了假的bfs 于是用到了dfs模板 寻找二分图最大匹配的算法是匈牙利算法 匈牙利算法的主要程序是寻找增广路 寻找增光路是过程是:从一个未经配对的点出发,历经未配边.匹配边.未配边.匹配边.未配边....最终到达一个未配点的过程,只要把路径中的未配边和匹配边的“身份”对调,匹配就加一了.这就是一个寻找增广路的过程,通过不断寻找增广路,可以找到最大的匹配. 1 #include<cstdio> 2 #include<cstring&g

算法笔记_139:二分图的最大权分配(Java)

目录 1 问题描述 2 解决方案   1 问题描述 何为二分图的最大权匹配问题? 最大权二分匹配问题就是给二分图的每条边一个权值,选择若干不相交的边,得到的总权值最大. 2 解决方案 对于此问题的讲解,引用文末参考资料1: 解决这个问题可以用KM算法.理解KM算法需要首先理解"可行顶标"的概念.可行顶标是指关于二分图两边的每个点的一个值lx[i]或ly[j],保证对于每条边w[i][j]都有lx[i]+ly[j]-w[i][j]>=0.如果所有满足lx[i]+ly[j]==w[i

小结:双连通分量 &amp; 强连通分量 &amp; 割点 &amp; 割边

概要: 各种dfs时间戳..全是tarjan(或加上他的小伙伴)无限膜拜tarjan orzzzzzzzzz 技巧及注意: 强连通分量是有向图,双连通分量是无向图. 强连通分量找环时的决策和双连通的决策十分相似,但不完全相同. 强连通分量在if(FF[v])后边的else if还要特判是否在栈里,即vis[v],然后才更新LL[u] 割点和强连通分量因为是无向图所以要判个fa,可以在dfs时维护个fa参数 割点如果要求分割的分量,那么就是这个节点对他的子树是割点的数目+1. 割点不需要栈维护但是

tarjan求割点割边的思考

这个文章的思路是按照这里来的. 首先来看求割点.割点必须满足去掉其以后,图被分割.tarjan算法考虑了两个: 一,根节点如果有两颗及以上子树,它就是割点.这个应该说是显然的. 二,对于普通的结点a,如果它递归树的子树中,有任意节点b的low[b]<dfn[a],那么它就不是割点,反之则是割点. 我们先来证明如果low[b]<dfn[a],a一定不是割点.low[b]<dfn[a]说明有一个结点,通过非树枝边可以访问到a以前的结点,那么显然去掉a以后,b依然与a以上的递归树联通,a不是割