强联通分量

写博客的好处之一就是能方便的找模板

这里介绍的是Tarjan写法。
维护两个数组, dfn[], low[]。对图进行dfs,dfn表示当前节点的时间戳, low表示以当前节点为子树中最小的时间戳。

  1. dfs访问每个节点,并将节点压入栈中,访问它的子节点。
  2. 子节点在栈中更新节点的low
  3. 子节点不在栈中递归访问子节点回溯更新low

判断一个联通分量的标志:dfn[u] = low[u]

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <stack>
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;

const int maxn = 1e5 + 10;
int dfn[maxn], low[maxn];
int cnt, tot, n, m;
int head[maxn];
int belong[maxn];
bool vis[maxn];
int ans;
stack<int>st;

struct node{
	int v, nex;
}edge[maxn * 2];

void init()
{
	while(!st.empty())
		st.pop();
	mem(dfn, 0), mem(low, 0);
	mem(vis, 0);
	mem(belong, 0);
	mem(head, -1);
	cnt = 0;
	ans = 0;
	tot = 0;
}

void addedge(int u, int v)
{
	edge[cnt].v = v;
	edge[cnt].nex = head[u];
	head[u] = cnt++;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++tot;
	vis[u] = 1;
	st.push(u);
	for(int i = head[u]; i != -1; i = edge[i].nex)
	{
		int v = edge[i].v;
		if(!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(vis[v])
		{
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(dfn[u] == low[u])
	{
		int x;
		ans++;
		while(1)
		{
			x = st.top();
			st.pop();
			vis[x] = 0;
			if(x == u)
				break;
		}
	}
}

int main()
{
	while(cin >> n >> m)
	{
		if(n == 0 && m == 0)
			break;
		init();
		for(int i =1; i <= m; i++)
		{
			int x, y;
			cin >> x >> y;
			addedge(x, y);
		}
		for(int i = 1; i <= n; i++)
			if(dfn[i] == 0)
				tarjan(i);
		cout << ans << " ";
		if(ans == 1)
			cout << "Yes" << endl;
		else
			cout << "No" << endl;
	}
	return 0;
}
发布了73 篇原创文章 · 获赞 15 · 访问量 8093

猜你喜欢

转载自blog.csdn.net/ln2037/article/details/103466615