Popular Cows POJ - 2186 有向图的双连通分量
Posted qingyuyyyyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Popular Cows POJ - 2186 有向图的双连通分量相关的知识,希望对你有一定的参考价值。
//对于一个有向图,连通分量:对于分量中任意两点uv,
//必然可以从u走到v,也可以从v走到u
//强连通分量(scc):极大连通分量,也就是加上任何一个点之后,都不是连通分量
//有向图通过缩点,转化为有向无环图(DAG),拓扑图
//缩点是指将所有连通分量缩成一个点
//Tarjan算法求scc
//对每个点定义两个时间戳
//dfn[u]表示遍历到u的时间戳
//low[u]表示从u开始走,所能遍历到的最小时间戳
//u是其所在强连通分量的最高点,等价于dfn[u]==low[u]
//时间复杂度O(n+m)
//缩点,先遍历所有点i
//再遍历i的所有邻点
//如果i和j不在同一个连通分量中,加一条新边,id[i]->id[j]
//那么就把图建出来了,有向无环图DAG
//按照拓扑序来做
//连通分量编号递减的顺序一定是拓扑序
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 10010, M = 50010;
int n, m;
int h[N], e[M], ne[M], idx;
int dfn[N], low[N], timestamp;
// 栈顶
int stk[N], top;
bool in_stk[N];
//属于哪个连通分量
int id[N];
//当前有多少强连通分量
int scc_cnt;
//每个强连通分量中点的数量
int Size[N];
//每个连通分量的出度
int dout[N];
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
void tarjan(int u)
{
//先都等于时间戳
dfn[u] = low[u] = ++ timestamp;
//把当前点加到栈当中去
//栈当中存的值,不单单是当前路径上的值,还可能存其他边上的点
//存的点,都不是它所在强连通分量的最高点
//都是,当前,还没有搜完的遍历完的,强连通分量的所有点
stk[ ++ top] = u;
//记录是否在栈当中
in_stk[u] = true;
//遍历u的所有临点
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
//如果还没有被遍历过
if (!dfn[j])
{
//遍历
tarjan(j);
//更新
low[u] = min(low[u], low[j]);
}
//否则,说明遍历过,而且还在栈当中
else if (in_stk[j])
//取最小
low[u] = min(low[u], dfn[j]);
}
//遍历完u之后,发现u能到的最前面的点就是自己了
if (dfn[u] == low[u])
{
//那就说明,u肯定是所在强连通分量的最高点
//所有强连通分量个数++
++ scc_cnt;
int y;
do
{
//先取出栈顶元素
y = stk[top -- ];
//表示出栈
in_stk[y] = false;
//标记当前点属于哪个强连通分量
id[y] = scc_cnt;
//
Size[scc_cnt] ++ ;
//y==u时,表示所在强连通分量处理完了
}
while (y != u);
}
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m -- )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
//建新图
for (int i = 1; i <= n; i ++ )
if (!dfn[i])
tarjan(i);
//统计所有点的出度
//遍历原图的所有边
for (int i = 1; i <= n; i ++ )
for (int j = h[i]; ~j; j = ne[j])
{
int k = e[j];
int a = id[i], b = id[k];
//从a走到b
//所有a的出度增加
if (a != b)
dout[a] ++ ;
}
int zeros = 0, sum = 0;
for (int i = 1; i <= scc_cnt; i ++ )
if (!dout[i])
{
//统计有多少个点的出度为0
//如果只有一个,那么最终答案就是出度为0的点的size
//如果大于一个,那么出度为0的强连通块必然不能互相到达
//所有就是0
zeros ++ ;
sum += Size[i];
if (zeros > 1)
{
sum = 0;
break;
}
}
printf("%d
", sum);
return 0;
}
以上是关于Popular Cows POJ - 2186 有向图的双连通分量的主要内容,如果未能解决你的问题,请参考以下文章
Popular Cows POJ - 2186(强连通分量)