图论(Tarjan算法与有向图)

一、基础

1、流图:给定有向图G,如果从G中一个顶点出发可以到达图中所有点,则称G是一个流图,记为(G,r),其中r称为流图的源点

2、强连通图:对一个有向图,如果图中任意两个结点x,y,既存在x到y的路径,也存在y到x的路径,则称该有向图是强连通图

3、强连通分量:有向图的极大连通子图被称为强连通分量,记为SCC

4、追溯值:x的追溯值low[x]定义为满足以下条件的节点的最小的时间戳(通过返祖边能到达的最小的值):

(1)、该点在栈中;(2)、存在一条从以x为根的子树出发的有向边,以该点为终点

计算方法:

(1)、当节点x第一次被访问时,把x入栈,初始化low[x]=dfn[x]

(2)、历遍x出发的每一条边(x,y):

如果y没被访问过,则说明(x,y)是树枝边(搜索树中的边),此时应递归访问y,从y回溯之后,令low[x]=min(low[x],low[y])

如果y被访问过且在栈中,则令low[x]=min(low[x],dfn[y])

(3)、从x回溯之前,判断是否有low[x]=dfn[x],若成立则不断从栈中弹出节点,直至x出栈

二、强连通分量的判定

在追溯值(low)计算过程中,若从x回溯前,由low[x]=dfn[x]成立,则栈中从x到栈顶的所有节点构成一个强连通分量

int head[maxn], to[maxn], nex[maxn], cntt;
int stk[maxn], len, instk[maxn];
int c[maxn], f[maxn];		//第i个点属于第c[i]个强连通分量
int dfn[maxn], low[maxn], tot;
vector<int>scc[maxn];
int cnt;
void add(int u, int v)
{
	to[++cntt] = v;
	nex[cntt] = head[u];
	head[u] = cntt;
}
void dfs(int x)
{
	dfn[x] = low[x] = ++tot;
	stk[++len] = x;
	instk[x] = 1;
	for (int i = head[x]; i; i = nex[i])
	{
		int y = to[i];
		if (!dfn[y])
		{
			dfs(y);
			low[x] = min(low[x], low[y]);
		}
		else if (instk[y])
		{
			low[x] = min(low[x], low[y]);
		}
	}
	if (dfn[x] == low[x])
	{
		cnt++;
		int y;
		do
		{
			y = stk[len--];
			instk[y] = 0;
			c[y] = cnt;
			scc[cnt].push_back(y);
		} while (y != x);
	}
}
void solve()
{
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= m; ++i)
	{
		int u, v;
		cin >> u >> v;
		add(u, v);
	}
	for (int i = 1; i <= n; ++i)
	{
		if (!dfn[i])
			dfs(i);
	}

	for (int i = 1; i <= cnt; ++i)
		sort(scc[i].begin(), scc[i].end());
	cout << cnt << endl;
	for (int i = 1; i <= n; ++i)
	{
		if (f[c[i]])
			continue;
		f[c[i]] = 1;
		for (int j = 0; j < scc[c[i]].size(); ++j)
		{
			cout << scc[c[i]][j] << " ";
		}
		cout << '\n';
	}
}

三、SCC缩点

缩点完成之后会得到一张有向无环图

//在求强连通分量的基础上
int sto[maxn << 1], snex[maxn << 1], shead[maxn], scnt;
void add_scc(int u, int v)
{
	sto[++scnt] = v;
	snex[scnt] = shead[u];
	shead[u] = scnt;
}

//以下代码加在main中
for (int i = 1; i <= n; i++)
{
	int y = to[i];
	if (c[i] == c[y])
		continue;
	add_scc(c[i], c[y]);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值