强连通:有向图中,如果任意2点都相互可达,则该图是强连通图。
强连通分量:有向图中,其强连通图子图,称为强连通分量。(缩点后每个点都原图中最大的强连通分量)
一个有向图是强连通的,等价于G中有一个回路,它至少包含每个节点一次。(只是一笔画经过所有点回到原点,点可以通过多次,不一定是一个大环,也可能是几个小环的拼接。但环上的所有点一定构成强连通分量)。
一些问题只要变成有向无环图就容易解决,但其中有环就比较难办,而环等价于强连通分量,把每个强连通分量缩成一个点,就是dag了。
常用算法是tarjan算法,复杂度是O(n+m),线性的。(注意有很多个tarjan算法..这个是求强连通的,还有离线求lca的,求双联通的..)
模板比较简单而且不怎么灵活,主要是一些推论。
比如一些简单推论:
从任一点出发都可以到达的点有几个?
缩点后如果出度为0的点集唯一,符合条件的点就是该点集中的点,否则不存在符合条件的点。(poj 2186)
最小点基:选择最少的点,使得从这些点出发可以到达所有点。
最小权点基:选择权和尽量小的点集,使得从这些点出发可以到达所有点。(hdu5934)
解法:入度为0的强连通分量个数即为最小点基,从每个入度为0的强连通分量中取出权值最小的点,构成的集合即最小权点基。
最少加多少边能使图变为强连通图?(poj1236)
Ans = max(缩点后入度为0的点集,缩点后出度为0的点集)(特判如果缩点后只有一个点则原图已经强连通,ans = 0)
以poj1236为例(求最小点基点数,及至少加几个点变成强连通图),比较模板的写法(主函数中只需要调用,缩点后的信息都有直接处理好,加边后跑一遍当做黑盒用就好..)
#include <bits/stdc++.h>
#define ll long long
#define mm 10005
using namespace std;
stack<int> sta;
bool vis[mm], in[mm], out[mm];
//vis点是否在栈中 缩点后的点是否有出度入度