强连通分量分解

知识背景:首先明确强连通分量(strongly connected component)的概念,从任一顶点能够到达任一其他顶点的有向图 的顶点子集,而任意有向图可以分解成若干不相交的scc。把每个scc视作一个顶点,可得到一个DAG。

实现算法:两次dfs,第一次 dfs 遍历将顶点后序(post order)记录下来vs(vector),这里需要注意的是,不管从哪个点dfs,由于是后序记录,故最终得到的记录是一样的,vs中的顶点(按下标)从前至后对应在DAG中为从尾到头。第二次dfs对于vs中的元素从尾到头进行,使用反向边,故每次dfs只能访问到同一个强连通分量。因而每次dfs时给所到顶点加上一个表示第几个scc的标号,即可完成对该图的分解。

例题:poj2186

每头羊有若干崇拜对象,并且崇拜关系可传递,求出被其他所有羊崇拜的羊的个数。

即求最后一个scc所含顶点个数。

#include<iostream>
#include<vector>
#include<cstring>

using namespace std;

int n,m;
vector<int> g[10001];
vector<int> rg[10001];
bool used[10001];
vector<int> vs;
int cmp[10001];

void dfs(int v) {
	used[v]=true;
	for(int i=0; i<g[v].size(); i++)
		if(!used[g[v][i]])
			dfs(g[v][i]);
	vs.push_back(v);
}

void rdfs(int v,int k) {
	used[v]=true;
	cmp[v]=k;
	for(int i=0; i<rg[v].size(); i++)
		if(!used[rg[v][i]])
			rdfs(rg[v][i],k);
}

int scc() {		//strongly connected component
	memset(used,0,sizeof(used));
	vs.clear();
	for(int i=0; i<n; i++)
		if(!used[i])
			dfs(i);
	int k=0;
	memset(used,0,sizeof(used));
	for(int i=vs.size()-1; i>=0; i--)
		if(!used[vs[i]])
			rdfs(vs[i],k++);        //最开始把vs[i]错写成i,把属于不同块的顶点打上了相同标号
	return k;
}

int main() {
	while(cin>>n>>m) {
		int to,from;
		for(int i=0; i<m; i++) {
			cin>>from>>to;
			g[from-1].push_back(to-1);
			rg[to-1].push_back(from-1);
		}
		int num=scc();
		int u,res=0;
		for(int i=0; i<n; i++) {
			if(cmp[i]==num-1) {
				res++;
				u=i;
			}
		}
		memset(used,0,sizeof(used));
		rdfs(u,0);
		for(int i=0; i<n; i++) {
			if(!used[i]) {
				res=0;
				break;
			}
		}
		cout<<res<<endl;
	}
	return 0;
}


另有一个Tarjan算法,运用了栈的思想,复杂度类似。可参考这篇,其中head数组表示以某个点为入点的一条边的下标,Struct edge中的next元素指向入点相同的其他边,第一条边的next为-1

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Absoler

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

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

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

打赏作者

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

抵扣说明:

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

余额充值