【图论】tarjan找有向图强连通分量 [USACO06JAN]牛的舞会The Cow Prom

tarjan找强连通分量


有向图强连通分量

在有向图G中,如果两个顶点vi,vj间(vi>vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。有向图的极大强连通子图,称为强连通分量。

tarjan找强连通算法

算法思想

首先,一个强连通分量形成一个环,或者是一个单点。
如何知道这个环上的某个点呢?即让这个点在dfs中被遍历到两次即可。
我们知道,图可以用dfs遍历,且dfs采用了“栈”的思想,可以用栈实现对强连通分量上的点的保存。一个点及其子孙中存在极大强连通分量,当且仅当该点是其与子孙各点中dfs序最小的那个,否则,若有一个子孙在它之前被遍历到,则能构成一个更大的强连通分量。

具体实现###

变量定义####

dfn[N]:某个点的dfs序
low[N]:某个点以及其子孙中dfs序最小的值
color[N]:某个点所属的强连通分量的颜色
st[N]:栈,用于储存可能构成强连通分量的点
vis[N]:某个点是否在栈中

代码####

void tarjan(int u)
{
	dfn[u]=low[u]=++deep;
    vis[u]=1;
    st[++top]=u;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[v],low[u]);
        }
        else
        {
            if(vis[v])
            low[u]=min(low[v],low[u]);
        }
    }
    if(low[u]==dfn[u])
    {
        vis[u]=0;
        color[u]=++tot;
        while(st[top]!=u)
        {
            vis[st[top]]=0;
            color[st[top]]=tot;
            top--;
        }
        top--;
    }
}

例题:[USACO06JAN]牛的舞会The Cow Prom

给你n个点,m条边,求图中所有大小大于1的强连通分量的个数

输入样例#1:

5 4

2 4

3 5

1 2

4 1

输出样例#1:

1

题解:数出强连通分量的个数,给每个强连通分量染色后,统计每种颜色的数量,输出颜色个数大于1的个数。

#include<cstdio>
#include<vector>
using namespace std;

const int maxn=10010;
int n,m;
int vis[maxn],dfn[maxn],low[maxn],color[maxn],t[maxn];
int deep;
int st[maxn],top;
int tot;

vector<int> e[maxn];

void tarjan(int u)
{
	dfn[u]=++deep;
	low[u]=deep;
	vis[u]=1;
	st[++top]=u;
	int sz=e[u].size();
	for(int i=0;i<sz;i++)
	{
		int v=e[u][i];
		if(!dfn[v])
		{
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(vis[v])
			low[u]=min(low[u],low[v]);
	}
	if(dfn[u]==low[u])
	{
		color[u]=++tot;
		vis[u]=0;
		while(st[top]!=u)
		{
			color[st[top]]=tot;
			vis[st[top--]]=0;
		}
		top--;
	} 
}
int ans;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		e[x].push_back(y);
	}
	for(int i=1;i<=n;i++)
		if(!dfn[i]) tarjan(i);
	for(int i=1;i<=n;i++)
		t[color[i]]++;
	for(int i=1;i<=tot;i++)
		if(t[i]>1) ans++;
	printf("%d\n",ans);
	return 0;
}
posted @ 2019-10-26 10:57  JWizard  阅读(109)  评论(0)    收藏  举报