Tarjan(scc)
Tarjan
算法之一是一个强连通分量算法,可以找出所有的强连通分量,时间复杂度线性。
算法中对于搜索树(如下图)作了如下定义:
- 树枝边,即父亲与孩子的边,如
。 - 前向边,即祖先到孩子的边,如
。(树枝边属于特殊的前向边) - 后向边,即孩子到祖先的边,如
。(与前向边相反) - 横叉边,当前点到已经结束的搜索分支,如
。(紫色部分表示已经结束的分支)
然后一个点在某个强连通分量内的情况:
- 走了一条后向边。
- 走了一条横叉边,再走了一条后向边。
我们定义
二者的体现就是就在于走的边
直观的感性理解:对于
栈中存储的是目前还没有完全找出的强连通分量。
void tarjan(int x){
dfn[x]=low[x]=++now;
in[x]=1;//表示在栈中
stk[++top]=x;
Ed{
int j=e[i];
if(!dfn[j]){
tarjan(j);
low[x]=min(low[x],low[j]);//由于是子节点,那么属于子树的范畴
}
else if(in[j])low[x]=min(low[x],dfn[j]);//由于是后向边/横叉边,所以不能再走,只能走到dfn(实际上写low好像也是对的)
}
if(low[x]==dfn[x]){//完整的强连通分量出现
++scc_cnt;
int y;
do{
y=stk[top--];
in[y]=0;
id[y]=scc_cnt;//表示每个点属于哪个强连通分量
}while(y!=x);
}
}
int main(){
E(i, n)
if(!dfn[i])
tarjan(i);//注意图可能不连通
}
经典例题:P2341 [USACO03FALL / HAOI2006] 受欢迎的牛 G
考虑先求强连通分量,这样一个强连通分量内的点就是两两喜欢,那么它们就同呼吸、共命运,要么都是明星,要么都不是。
我们定义缩点为一个强连通分量内的点视作一个点(原来再两个强连通分量之间的边保留,在一个强连通分量的边删去,并保留新点中有几个原点)。
此时若存在明星,必定证明只有一个点没有出边(如果超过
所以我们不需要建图,只需要检查出度为
代码
结论:对于一个有向图,加最少几条边可以边乘强连通分量?
- 入度为1与出度为1的点个数最大值
本文作者:wscqwq
本文链接:https://www.cnblogs.com/wscqwq/p/17606451.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步