1. 强连通分量:图论中的"朋友圈"
第一次接触强连通分量这个概念时,我把它想象成微信里的朋友圈。假设每个人都是一个顶点,A愿意把资料分享给B就画一条有向边。那么强连通分量就是这个圈子里互相都能看到动态的好友群——A能传给B,B能传给C,C又能传回给A。在信息学奥赛的刻录光盘问题里,这种"朋友圈"特性直接决定了需要多少张光盘。
实际解题时,我们需要先构建整个社交网络的有向图。比如洛谷P2835这道题,输入数据就是每个人的分享列表。这里有个小技巧:可以用邻接表存储图结构,C++里用vector<int> g[N]特别方便。我当年第一次做这题时,就因为用二维数组存图导致内存爆炸,后来改用邻接表瞬间清爽多了。
2. 三大算法对决:如何找出所有"朋友圈"
2.1 Kosaraju算法:正反两次DFS的魔法
Kosaraju算法就像玩迷宫游戏时先记下出口路线。具体步骤是:
- 在原图上做DFS,记录顶点结束时间
- 将图的所有边反向
- 按结束时间倒序在反图上DFS
这个算法最妙的地方在于第二次DFS时,每次调用就能找到一个完整的强连通分量。代码实现时要注意用栈来保存顶点顺序,我经常在这里犯糊涂把入栈顺序搞反。时间复杂度O(V+E)让它成为稀疏图的优选。
2.2 Tarjan算法:一趟DFS搞定所有
Tarjan算法更聪明,它通过维护dfn和low数组,在单次DFS中就能识别强连通分量。dfn记录访问顺序,low记录能回溯到的最早节点。当dfn[u]==low[u]时,栈中从u往上的节点就构成一个强连通分量。
实际编码时容易踩的坑是忘记维护inStack数组。有次比赛我就因为这个bug调试了半小时,最后发现是弹栈时没更新inStack状态。Tarjan算法同样O(V+E)的复杂度,但常数更小,是我现在的首选。
2.3 Floyd+并查集:暴力美学
对于不擅长DFS的同学,Floyd求传递闭包配合并查集是个另类选择。虽然O(V³)的复杂度看着吓人,但在V≤200时完全够用


245

被折叠的 条评论
为什么被折叠?



