tarjan求点的双连通分量

hiho详解链接:http://hihocoder.com/contest/hiho55/problem/1


点的双连通分量的定义:对于一个无向图的子图,当删除其中任意一个点后,不改变图内点的连通性,这样的子图叫做点的双连通子图。而当子图的边数达到最大时,叫做点的双连通分量。


可以知道的是,桥一定是点的双连通分量。


void pop_stack(int x)//边不断出栈并标记,直到编号x的边离开栈为止
{
	num_node++;
	int len = 0,minn = x;
	e_vis[x] = 1;
	while(top > 0 && sta[top] != x)
	{
		fuben[len++] = sta[top];
		e_vis[sta[top]] = 1;//不断标记边
		minn = min(minn,sta[top]);//本题要求,记录同一组内边标号的最小值,可省略
		top--;
	}
	top--;
	fuben[len++] = x;
	for(int i = 0;i < len;i++)
		mark[fuben[i]] = minn;//标记原边所在的组,可省略
}
//dfs_pos为dfs序,low为最近祖先的dfs序,这里的边中要多加个变量id来便于边的标记
//vis为这个点有没被访问
void tarjan(int u,int pre)
{
	int son_num = 0;
	int v;
	vis[u] = 1;
	dfs_pos[u] = low[u] = ++jilu;//jilu为从的dfs序
	for(int i = head[u];~i;i = e[i].nex)
	{	
		v = e[i].v;
		if(e_vis[e[i].id])continue;//如果边被访问过则跳过,
		if(!vis[v]){
			sta[++top] = e[i].id;
			son_num++;
			tarjan(v,u);
			low[u] = min(low[u],low[v]);
		  /*if(pre == -1 && son_num > 1){
				pop_stack(e[i].id);	
			}*///这题并不需要这句话,某些题目判断时可能需要
			if(low[v] >= dfs_pos[u]){
				pop_stack(e[i].id);//边的出栈处理操作
			}
		}else if(v != pre){
			sta[++top] = e[i].id;
			low[u] = min(low[u],low[v]);
		}
	}
}



hiho原题的题解代码:

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
typedef long long ll;
using namespace std;
const int maxn = 250005;
struct ppp
{
	int v,nex,id;
}e[maxn];
int head[maxn / 10],tole,n,m,vis[maxn / 10],jilu;
int dfs_pos[maxn / 10],low[maxn / 10];
int mark[maxn],e_vis[maxn],top;
void make_edge(int u,int v,int id)
{
	e[tole].id = id;e[tole].v = v;e[tole].nex = head[u];head[u] = tole++;
}
int sta[maxn];
int fuben[maxn];
int num_node;
void pop_stack(int x)//边不断出栈并标记,直到编号x的边离开栈为止
{
	num_node++;
	int len = 0,minn = x;
	e_vis[x] = 1;
	while(top > 0 && sta[top] != x)
	{
		fuben[len++] = sta[top];
		e_vis[sta[top]] = 1;//不断标记边
		minn = min(minn,sta[top]);//本题要求,记录同一组内边标号的最小值,可省略
		top--;
	}
	top--;
	fuben[len++] = x;
	for(int i = 0;i < len;i++)
		mark[fuben[i]] = minn;//标记原边所在的组,可省略
}
//dfs_pos为dfs序,low为最近祖先的dfs序,这里的边中要多加个变量id来便于边的标记
//vis为这个点有没被访问
void tarjan(int u,int pre)
{
	int son_num = 0;
	int v;
	vis[u] = 1;
	dfs_pos[u] = low[u] = ++jilu;//jilu为从的dfs序
	for(int i = head[u];~i;i = e[i].nex)
	{	
		v = e[i].v;
		if(e_vis[e[i].id])continue;//如果边被访问过则跳过,
		if(!vis[v]){
			sta[++top] = e[i].id;
			son_num++;
			tarjan(v,u);
			low[u] = min(low[u],low[v]);
		  /*if(pre == -1 && son_num > 1){
				pop_stack(e[i].id);	
			}*///这题并不需要这句话,某些题目判断时可能需要
			if(low[v] >= dfs_pos[u]){
				pop_stack(e[i].id);//边的出栈处理操作
			}
		}else if(v != pre){
			sta[++top] = e[i].id;
			low[u] = min(low[u],low[v]);
		}
	}
}

int main()
{
	while(~scanf("%d%d",&n,&m))
	{
		tole = 0;mem(head,-1);mem(vis,0);mem(e_vis,0);
		num_node = 0;
		for(int i = 1,a,b;i <= m;i++)
		{
			scanf("%d%d",&a,&b);
			make_edge(a,b,i);
			make_edge(b,a,i);
		}
		top = 0;jilu = 0;mem(mark,0);
		tarjan(1,-1);
		printf("%d\n",num_node);
		for(int i = 1;i <= m;i++)
		{
			if(i > 1)printf(" ");
			cout<<mark[i];
		}
		printf("\n");
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章