Tarjan(强连通模板)

强连通分量:

    1)有向图中,该图中的任意两点之间可互达。

    2)一个一个点也是强连通分量

两个概念:

 1)时间戳 dfn[ x ] 

​      时间戳是用来标记图中每个节点在进行深度优先搜索时被访问的时间顺序,当然,你可以理解成一个序号(这个序号由小到          大),用 dfn[x] 来表示(搜到该点的最早时间)

2)low数组

       2)low数组 : low[x] 表示 x是从哪个点(这个环中最早遍历到的那个点的 low 值 ) 组成的环,如果这几个点组成一个环,则这几个点 low值 都是相同,因为他们从某个点开始遍历,最终还能回到这个点。

整体思想

每次搜一个结点 初始化 dfn [x] = low [x] = ++tot 如果该节点没有被访问过就DFS访问他的出边,然后并入栈,在回溯的过程中更新该点的low[x]值  low[x] = min(low[x], low[y] ),看x 和 y 都是以哪个点为根节点的环。如果 y 点被访问过且还在栈中(说明这个点是强联通分量的中的一个点)继续更新low值 Low[ x ] = min(low[x], dfn[y]) 看谁出现的时间更早,不在栈中说明已经是其他或者自己成为了一个强连通分量

回溯时 dfn [ x ] = low [ x ] 时 // x 这个点就是 强联通分量的根,然后依次从栈内弹出元素,直到x == 栈顶元素(找到了环的根)就退出 找到了图中的一个强联通分量

代码:

const int MAXN = 1e5 + 10;
struct Edge{
	int to, next, dis;
}edge[MAXN << 1];
int head[MAXN], cnt, ans; 
bool inStack[MAXN]; 	//判断是否在栈中 
//dfn 第一次访问到该节点的时间(时间戳)
//low[i] low[i]能从哪个点(最早时间戳)到达这个点的。 
int dfn[MAXN], low[MAXN], tot;	
stack<int> stc;
void add_edge(int u, int v, int dis) {
	edge[++cnt].to = v;
	edge[cnt].next = head[u];
	head[u] = cnt;
}
void Tarjan(int x) {
	dfn[x] = low[x] = ++tot;
	stc.push(x);
	inStack[x] = 1;
	for(int i = head[x]; i; i = edge[i].next) {
		int to = edge[i].to;
		if ( !dfn[to] ) {
			Tarjan(to);
			low[x] = min(low[x], low[to]);
		} else if (inStack[to]){
			low[x] = min(low[x], dfn[to]);
		}
	}
	//cout << x << " " << low[x] << " " << dfn[x] << endl;
	if(low[x] == dfn[x]) {	//发现是整个强连通分量子树里 的最小根。
		//int cnt = 0;
		ans++;	//强连通分量计数器 
		while(1) {
			int top = stc.top();
			stc.pop();
			//cnt ++;
			inStack[top] = 0;
			//cout << top << " "; 每个强连通分量内的点 
			if(top == x) break;
		} 
	}
}
void init() {
	cnt = 1;
	tot = 0;
	ans = 0;
	memset(inStack, 0, sizeof(inStack));
	memset(head, 0, sizeof(head));
	memset(dfn, 0, sizeof(dfn));
	memset(low, 0, sizeof(low));
	while(!stc.empty()) stc.pop();
}
int main () {
	std::ios::sync_with_stdio(false);
	cin.tie(0);
	int n, m;
	while(cin >> n >> m && (n || m)){
		init();
		int x, y;
		for(int i = 1; i <= m; ++i) {
			cin >> x >> y;
			add_edge(x, y, 0);	//有向图求强连通 
		} 
		for(int i = 1; i <= n; ++i) {
			if( !dfn[i] )
				Tarjan(i);
		} 
	}
	return 0;
} 

模板题:

HDU-1269

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值