tarjan强连通分量SCC学习笔记

本文详细介绍了Tarjan算法在求解有向图强连通分量中的应用,包括变量dfn和low的含义、核心流程及典型应用场景。通过DFS遍历,利用dfn和low数组确定强连通分量,并给出了相应的C++代码实现。算法常与缩点技术结合,简化有向图结构,便于进行DP和拓扑排序等操作。
摘要由CSDN通过智能技术生成

tarjan强连通分量SCC学习笔记

变量释义

t a r j a n tarjan tarjan算法的两个核心数组 d f n dfn dfn数组和 l o w low low数组。
dfn数组:节点在 d f s dfs dfs树上的序号(相当于一个时间戳的作用)。
low数组:节点在 d f s dfs dfs树上的子树(不完全是子树,要扣掉已经分配到强连通分量的点)中,所能返回的点的 d f n dfn dfn的最小值(相当于找强连通分量的根)。
l o w low low数组比较难懂,可以意会一下,强连通分量的关键是互相可达, d f s dfs dfs的作用相当于就是祖先到子孙的可达,而 l o w low low数组就是让子孙尽可能地与更上层的祖先可达,从而达到互相可达的作用。
因此可以得出这样一个性质:强连通分量的根满足
d f n [ x ] = l o w [ x ] dfn[x]=low[x] dfn[x]=low[x]
注:强连通分量的根指强连通分量中dfn最小的点。

核心流程

tarjan算法的核心流程包括三个部分 d f s dfs dfs确定 d f n dfn dfn数组,取子树中(尚未分配到强连通分量)low值最小的点,确定每个强连通分量的根与其余点。
d f s dfs dfs不必多说
取low值最小如何做 d f s dfs dfs的过程中顺便更新为儿子的 l o w low low的最小值(有点树形dp的味道)。
如何判断是否分配到强连通分量?比较快捷的方法就是用一个SCC记录点所属的强连通分量的序号。
但是为了下一步确定强连通分量中的所有点,这里用栈来存搜索到了但未分配的点。
最后一步,确定SCC,当子树搜索完但该点low值没有变化时,该点即为强连通分量的根,弹出所有栈中的元素直到它本身,作为强连通分量(类比一下 d f s dfs dfs确定该点 d f s dfs dfs子树中的点的流程)。
代码如下:
变量声明:

vector <int> v[1005];//邻接表
stack <int> s;
int in_stack[1005]; //判断是否在栈中
int visit[1005];    //判断是否访问过
int dfn[1005];  //dfn指代i在dfs序列的位置
int low[1005];  //low数组指代该连通分量的根节点位置
int count1,count2;  //count1用于dfn,low的编号,count2用于强连通分量的编号
int scc[1005];  //编号为i的点所在的强连通分量的序号
int scc1[1005];     //编号为i的强连通分量的节点数目
vector <int> v1[1005];  //将每个连通分量的点都放到向量中

核心函数:

void tarjan(int now){
    dfn[now]=low[now]=++count1;
    in_stack[now]=1;
    visit[now]=1;
    s.push(now);
    for(int i=0;i<v[now].size();i++){
        if(!visit[v[now][i]]){      //v未访问,访问v,low值取较小的
            tarjan(v[now][i]);
            low[now]=min(low[now],low[v[now][i]]);
        }
        else if(in_stack[v[now][i]]){   //访问但未出栈,low值取较小的
            low[now]=min(low[now],low[v[now][i]]);
        }
    }
    if(low[now]==dfn[now]){  //now为连通分量根节点,出栈直至now,把它们都放到同一个连通分量中
        while(s.top()!=now){
            scc[s.top()]=count2;
            scc1[count2]++;
            v1[count2].push_back(s.top());
            in_stack[s.top()]=0;
            s.pop();
        }
        scc[s.top()]=count2;
        scc1[count2]++;
        v1[count2].push_back(s.top());
        in_stack[s.top()]=0;
        s.pop();
        count2++;
    }
    return;
}

主函数内:

for(int i=1;i<=n;i++){
        if(!visit[i]){
            tarjan(i);
        }
    }

典型应用

t a r j a n tarjan tarjan算法一般配合缩点来使用,缩点后容易观察到,有向图成为有向无环图,可以进行dp,拓扑排序等操作。
推荐几个例题,方法都差不多。
洛谷P2863 模板
洛谷P3387 配dp使用
洛谷P2746 有一定思维难度

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值