这个问题来自于《算法概论》的课后习题,我想了2天,最后想出一个可行的办法。
首先,对于环的问题,有两个常规的手段,一个是点染色,还有一个是利用边的分类。边的分类主要是确定出环的存在性(或位置),而染色,则是区分奇环和偶环的关键,这也是我最后才想到的。
假定强连通有向图G的根是r,设r为红色,那么,我们从r出发,每走一步,就把脚下的点染色为上一个点的相反的颜色(红变蓝,蓝变红),并且,曾经染色过的点可以重复染色。假设G有一个奇环,设这个奇环为v0,v1,v2.。。vn,v1.。。。然后,我们会发现,存在一个从r出发的路径,使得v1被染色为红色,同时,也绝对存在一个从r出发的路径,使得v1被染色为蓝色。原因是,v1处于一个奇环当中,我们可以借助它来实现这个效果。反过来说,假设存在一个从r出发的路径,使得某个点vk被染色为红色,同时也存在一个从r出发的路径,使得vk被染色为蓝色,那么,我们可以断定,必定存在一个包含vk的奇环,这个结论也不难理解。所以,我们就证明了以下结论:
强连通有向图G含有奇环 等价于
从r出发,可以使得某个vk被染色为红色,并且也可以被染色为蓝色。
如果某个vk既可以是红色,又可以是蓝色,就意味着,它的前继结点也满足这个有趣的性质:既可以是红色也可以是蓝色。把这个性质一直往前推,就可以知道,r必定既可以是红色又可以是蓝色!
如果一条路径会使得某个点曾经被染色为红色和蓝色,那么这个路径我们称为有趣路径。
由上面的讨论知道,有趣路径的存在性与奇环的存在性,是等价的。
然后,我们从边的分类去分析。
假设T是G的dfs生成树。显然,T里的点是红蓝相间,梅花间竹的。
也就是说,如果没有那些前向边,反向边和横插边,无论我们怎么走,都走不出一条有趣路径。然后,我们逐条边地往T上面增加。
假如我们增加的是前向边,
(1)前向边的两个端点颜色相反,还是没有有趣路径。
(2)否则,马上就可以得到一条有趣路径。
假如我们增加的是前向边,
(1)前向边的两个端点颜色相反,还是没有有趣路径。
(2)否则,马上就可以得到一条有趣路径。
假如我们增加的是反向边,
(1)反向边的两个端点颜色相反,还是没有有趣路径。
(2)否则,马上就可以得到一条有趣路径。
假如我们增加的是横叉边,
(1)横叉边的两个端点颜色相反,还是没有有趣路径。
(2)否则,马上就可以得到一条有趣路径。
可以发现,只要碰到了两端点颜色相同的某条非树边,就马上可以确定一条有趣路径,从而马上知道奇环的存在。
反过来说,如果奇环存在,就一定可以找到两端点颜色相同的某条非树边,因为找不到的话,就肯定不存在有趣路径,也就不存在奇环了。
所以,这个寻找强连通图G的奇环的算法就是:
对G进行dfs,同时染色,如果发现两端点颜色相同的某条非树边,就说明有奇环,不然,就说明没有奇环。
题外话:
图的问题,拆分强连通分支,边的分类,删除边,增加边(特别是往dfs和bfs树上面进行的增加边操作),删除点,增加点,还有染色,记录访问和离开时间,某个子图的首个被访问的点,这些都是很重要的思考方向。