【图论】有向图的强连通分量

本文介绍了如何通过Tarjan算法求解有向图的强连通分量,包括强连通分量的定义、作用以及在实际问题中的应用,如网络流行度计算、拓扑排序和最大半连通子图。展示了算法步骤和在牛学校网络问题、最受欢迎牛的数量计算以及最大半连通子图中的具体运用。

有向图的强连通分量

连通分量: 对于分量中任意两点 u , v u,v u,v,必然可以从 u u u走到 v v v,且从 v v v走到 u u u
强连通分量( S C C SCC SCC): 极大连通分量。一个连通分量加上任何一些点都不是连通分量了,该连通分量就是强连通分量。
强连通分量的作用: 将任意有向图通过 缩点(将所有连通分量缩成一个点) 转换成有向无环图( D A G DAG DAG)。
在这里插入图片描述
常见应用:对于上图,将有向图缩点之后,可以直接按照拓扑序递推来求最短路/最长路

如何求强连通分量( T a r j a n Tarjan Tarjan算法)

D F S DFS DFS
一些概念:
边可以分为四大类:
1.树枝边 ( x , y ) (x,y) (x,y) x x x y y y的父节点
在这里插入图片描述

2.前向边( x x x, y y y)。 x x x y y y的祖先节点
在这里插入图片描述
3.后向边( x x x, y y y)
在这里插入图片描述
4.横叉边(往之前搜过的其他分支搜,连向其他分支的边)
在这里插入图片描述
如果一个点在强连通分量( S C C SCC SCC)中
情况1:存在一条后向边,指向祖先结点
情况2:先走到横叉边,横叉边再走到祖先节点

Tarjan算法求强连通分量( S C C SCC SCC)
引入时间戳的概念,在搜索的时候给每一个点一个编号(按照深度优先搜索的顺序)
在这里插入图片描述
对每个点定义两个时间戳:
d f n [ u ] dfn[u] dfn[u]表示遍历到 u u u的时间戳
l o w [ u ] low[u] low[u]表示从 u u u开始走,所能遍历到的最小的时间戳
u u u是其所在的强连通分量的最高点,等价于 d f n [ u ] dfn[u] dfn[u]== l o w [ u ] low[u] low[u]
tarjan算法模板

void tarjan(int u){
   
   
    //dfn是当前点的时间戳,low是该点能够到达的最小的时间戳
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;//将当前点加入栈当中
    for(int i=head[u];~i;i=ne[i]){
   
   //遍历u所有能到的点
        int v=e[i];
        if(!dfn[v]){
   
   //如果该点还没有被遍历过
            tarjan(v);
            low[u]=min(low[u],low[v]);//更新最小的时间戳
        }
        else if(in_stk[v])low[u]=min(low[u],dfn[v]);//如果v点还在栈中,就用这个点来更新low值
    }
    if(dfn[u]==low[u]){
   
   //u是该强连通分量的最高点
        ++scc_cnt;
        int y;
        do{
   
   
            y=stk.top();stk.pop();//将该强连通分量的所有点出栈
            in_stk[y]=false;//不在栈中了
            id[y]=scc_cnt;//标记该点的强连通分量的下标
            Size[scc_cnt]++;//该连通分量的大小加1
        }while(y!=u);
    }
}

时间复杂度 O ( n + m ) O(n+m) O(n+m)

//缩点
for i=1;i<=n;i++
 for i的所有邻点j
   if i和j不在同一scc中:
    加一条新边id[i]→id[j]

形成一个有向无环图 ( D A G ) (DAG) (DAG)
缩点之后,强连通分量编号点按编号递减的顺序就是拓扑序。
假设对于一个点 u u u,当我们执行if(dfn[u]==low[u])这句话时,说明 u u u点所能到的点都搜完了,也就是这个点的所有后继都搜完了,我们才将这个点所在序列当中(也就是标记为 s c c _ c n t scc\_cnt scc_cnt),逆序来看的话,所有这个点的后继都在这个点的前面,那必然就是拓扑序了。

受欢迎的牛

原题链接
每一头牛的愿望就是变成一头最受欢迎的牛。
现在有 N N N头牛,编号从 1 1 1 N N N,给你 M M M对整数 ( A , B ) (A,B) (A,B),表示牛 A A A认为牛 B B B受欢迎。
这种关系是具有传递性的,如果 A A A认为 B B B受欢迎, B B B认为 C C C受欢迎,那么牛 A A A也认为牛 C C C受欢迎。
你的任务是求出有多少头牛被除自己之外的所有牛认为是受欢迎的。

输入格式
第一行两个数 N , M N,M N,M
接下来 M M M行,每行两个数 A , B A,B A,B,意思是 A A A认为 B B B是受欢迎的(给出的信息有可能重复,即有可能出现多个 A , B A,B A,B)。

输出格式
输出被除自己之外的所有牛认为是受欢迎的牛的数量。

数据范围
1 ≤ N ≤ 1 0 4 1≤N≤10^4 1N104,
1 ≤ M ≤ 5 × 1 0 4 1≤M≤5×10^4 1M5×104

输入样例:

3 3
1 2
2 1
2 3

输出样例:

1

样例解释
只有第三头牛被除自己之外的所有牛认为是受欢迎的。

分析:
题目意思就是需要找到的牛是能够被其他所有牛所能到达的,直接暴力的话时间复杂度太大。
如果用拓扑图来说,这个题会变得简单,如果当前图是一个拓扑图,如果至少存在两个终点(没有出边,出度为 0 0 0),那么这两个终点不可达,那么答案为 0 0 0;如果只有一个点出度为 0 0 0,那么所有点都能走到这个点。所以对于拓扑图,我们只需要判断有几个出度为 0 0 0的点即可。那么本题,我们将一个图的强连通分量缩点成拓扑图,再进行判断。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=10005,M=50005;
int n,m;
int head[N],e[M],ne[M],tot;
int dfn[N],low[N],timestamp;
stack<int>stk;
bool in_stk[N];
int id[N],scc_cnt,Size[N];
int dout[N];
void add(int a,int b){
   
   
    e[tot]=b,ne[tot]=head[a],head[a]=tot++;
}
void tarjan(int u){
   
   
    //dfn是当前点的时间戳,low是该点能够到达的最小的时间戳
    dfn[u]=low[u]=++timestamp;
    stk.push(u),in_stk[u]=true;
    for(int i=head[u];~i;i=ne[i]){
   
   
        int v=e[i];
        if(!dfn[v]){
   
   //如果该点还没有被遍历过
            tarjan(v);
            low[u]=min(low[u],low[v]);//更新最小的时间戳
        }
        else if(in_stk[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
   
   //u是该连通分量的最上面的点
        ++scc_cnt;
        int y;
        do{
   
   
            y=stk.top();stk.pop();//将该连通分量的所有点出栈
            in_stk[y]=false;//不在栈中了
            id[y]=scc_cnt;//标记该点的连通分量的下标
            Size[scc_cnt]++;//该连通分量的大小加1
        }while(y!=u);
    }
}
int main()
{
   
   
    scanf("%d %d",&n,&m);
    memset(head,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a碟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值