【有向图强连通分量(SCC)】

Tarjan算法(O(n+m))】

#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int n, pre[MAXN], sccno[MAXN], dfs_clock, scc_cnt;
//sccno[1~n]标记每个点在第几个SCC中,scc_cnt是SCC的计数,dfs_clock递归时间戳
vector<int> g[MAXN];
stack<int> s;

int dfs(int u)
{
	int lowu = pre[u] = ++dfs_clock, sz = g[u].size();
	s.push(u);
	rep(i,0,sz)
	{
		int v = g[u][i];
		if(!pre[v])
		{
			int lowv = dfs(v);
			lowu = min(lowu, lowv);
		}
		else if(!sccno[v]) lowu = min(lowu, pre[v]);
	}
	if(lowu == pre[u])//u的子孙最早只能回到u,说明找到一个SCC分量
	{
		int x;
		scc_cnt++;
		do{
			x = s.top();s.pop();
			sccno[x] = scc_cnt;
		}while(x != u);
		/*可以对这个强连通分量操作*/
	}
	return lowu;
}
void find_scc()
{
	dfs_clock = scc_cnt = 0;
	clc(pre,0);
	clc(sccno,0);
	repe(i,1,n) if(!pre[i]) dfs(i);
}


【Kosaraju算法(O(n+m)常数比Tarjan大)】

#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
#define min(a,b) (a>b?b:a)
#define MAXN 10010
int sccno[MAXN], vis[MAXN], scc_cnt, n;
vector<int> g[MAXN], gt[MAXN], s;
//gt[]是g[]的相反边的图

void dfs1(int u)//获取拓扑序
{
	vis[u] = true;
	int sz = g[u].size();
	rep(i,0,sz) if(!vis[g[u][i]]) dfs1(g[u][i]);
	s.push_back(u);
}
void dfs2(int u)
{
	sccno[u] = scc_cnt;
	int sz = gt[u].size();
	rep(i,0,sz) if(!sccno[gt[u][i]]) dfs2(gt[u][i]);
}
void find_scc()
{
	scc_cnt = 0;
	s.clear();
	clc(sccno,0);
	clc(vis,0);
	repe(i,1,n) if(!vis[i]) dfs1(i);
	per(i,n-1,0) if(!sccno[s[i]]) scc_cnt++, dfs2(s[i]);
}


 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值