【图的连通】Tarjan

千古神犇Tarjan,扑通扑通跪下来 sto sto Tarjan orz orz 强联通分量: 思路其实很简单就是dfs跑跑跑,看能不能返祖为一个圈。具体实现并不需要top排序(只是top排序之后可以保证最少次数的从根节点(搜索树根)开始跑tarjan)
void tarjan(int x)
{
	dfn[x]=low[x]=++dfsx; sta[++top]=x; inst[x]=1;
	int ss=ve[x].size();
	for(int i=0;i<ss;i++)
	{
		if(!dfn[ve[x][i]])
		{
			tarjan(ve[x][i]);
			low[x] = min(low[x],low[ve[x][i]]);
		}
		else if(inst[ve[x][i]]) low[x] = min(low[x],dfn[ve[x][i]]);
	}
	int to;
	if(low[x]==dfn[x])
	{
		++scc;
		do{ 
		to = sta[top];
		inst[to]=0;
		blo[to]=scc;
		top--;
		}while(to!=x);
	}
}

以下针对Kosaraju不能解决的众多无向图问题

求割点的无向图tarjan:写法和tarjan差不多,但判low时边不能往父亲判,当一个结点u为割点的条件为对于自己的搜索子结点v,有low[v]>=dfn[u],或者作为根结点,在搜索树(!!!)中有两个或以上的子结点(即从这个点开始可以跑两次tarjan)。 求桥(割边)的无向图tarjan,和求割点差不多,当一条边为树当且仅当对于子结点连的边<u,v>有low[v]>dfn[u]。
bool ispoint[];//割点
void tarjan(int x,int fu)
{
	dfn[x] = low[x] = ++dfnx;
	fa[x] = fu; if(x==1)int child = 0; 
	int ss=ve[x].size();
	for(int i=0;i<ss;i++)
	{
		if(!dfn[ve[x][i]])
		{
			if(x==1) child++;
			tarjan(ve[x][i],x);  
			low[x]=min(low[x],low[ve[x][i]]);
			if(low[ve[x][i]]<=dfn[x]&&x!=1) ispoint[ve[x][i]]=1;
			if(low[ve[x][i]]>dfn[x]) 
			{
				printf("from %d to %d is bridge",&x,&ve[x][i]);//桥
			} 
		}
		else if(ve[x][i]!=fu)
		{
			low[x] = min(low[x],dfn[ve[x][i]]);
		}
	}
	if(x!=1) return;
	if(child>=2)
	{
		ispoint[x]=1;
	}
}
一些概念: 点双连通分量:不含割点的极大连通子图 点双连通分量很好找呀,在我们徐照每次找到一条边或反向边就把这条边加入栈,当发现割点<u,v>边,我们就一个个出栈,直到边<u,v>,取出来这些点就是一些点双。 割点可以属于多个点双连通分量,其他点和每条边只属于一个点双。
bool ispoint[];//割点
void tarjan(int x,int fu)
{
	stack<int>s; 
	dfn[x] = low[x] = ++dfnx;
	fa[x] = fu; if(x==1)int child = 0; 
	for(int it=la[x];it;it=nt[it])
	{
		if(!dfn[en[it]])
		{
			s.push(it);
			if(x==1) child++;
			tarjan(en[it],x);  
			low[x]=min(low[x],low[en[it]]);
			if(low[en[it]]<=dfn[x])
			{
				ispoint[x]=1;
				bcc++;
				while(233)
				{
					int k=s.top(); s.pop();
					int x=st[k]; int y=en[k];
					if(bl[x]!=bcc)
					{
						blo[bcc].push_back(x); bl[x]=bcc;
					}
					if(bl[y]!=bcc)
					{
						blo[bcc].push_back(y); bl[y]=bcc;
					}
					if(k==it) break;
				}
			}
		}
		else if(en[it]!=fu)
		{
			s.push(it);
			low[x] = min(low[x],dfn[ve[x][i]]);
		}
	}
	if(x!=1) return;
	if(child<2)
	{
		ispoint[x]=0;
	}
}
  边双连通分量:不含割边的极大连通子图 将所有的边求出了后,直接将所有的割边全部删除,然后原本的图就分成了若干个连通的块块,那么对于每个块就是一个边双。 桥不属于任何边双,对于其他的点和边只属于一个边双。 代码这个就不给了owo。
posted @ 2018-05-26 08:57  Newuser233  阅读(10)  评论(0)    收藏  举报