POJ3352 Road Construction 双连通分量和桥 tarjan

题意:

给一个无向图,问你需要添加多少条边之后这个图变成双连通分量。

关于桥和双连通分量之类的可以参看以下链接。

http://www.byvoid.com/blog/biconnect/ 和http://blog.csdn.net/geniusluzh/article/details/6619575


思路:

/*一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。*/


#include<iostream>
#define min(a,b) (a<b?a:b)
using namespace std;
const int N=1005,M=2005;
int n,m;
struct Edge
{
	int v,next;
}edge[M];
int edgehead[N];
int k=1;
void addedge(int u,int v)
{
	edge[k].next=edgehead[u];
	edge[k].v=v;
	edgehead[u]=k++;
}
int dfn[N];
int low[N];
int index;
void tarjan(int i,int fa)//注意与求强连通分量时的tarjan算法的区别。
{
	dfn[i]=low[i]=index++;
	for(int j=edgehead[i];j;j=edge[j].next)
	{
		int v=edge[j].v;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[i]=min(low[i],low[v]);
			
		}
		else if(v!=fa)//这里的区别,不需要考虑是否在栈内。
		{
			low[i]=min(low[i],dfn[v]);
		}
	}
}
int degree[N];
void solve()
{
	tarjan(1,-1);//因为给定的图已经是强连通图。
	
	for(int i=1;i<=n;i++)
	{
		for(int j=edgehead[i];j;j=edge[j].next)
		{
			int v=edge[j].v;
			if(low[v]!=low[i])//在同一个双连通分量里面的low值相等
			{	
				degree[low[i]]++;//间接缩点了,记得这里不是degree[i]++;
				degree[low[v]]++;
			}
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(degree[i]==2)
			ans++;
	}
	printf("%d\n",(ans+1)/2);//这里要注意,因为缩点后是一棵树,双连通分量是树的叶子节点的数目+1除2。
}
int main()
{
	
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		index=1;
		memset(edge,0,sizeof(edge));
		memset(edgehead,0,sizeof(edgehead));
		k=1;
		memset(low,0,sizeof(low));
		memset(dfn,0,sizeof(dfn));
		memset(degree,0,sizeof(degree));
		int from,to;
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&from,&to);
			addedge(from,to);
			addedge(to,from);
		}
		solve();
	}
	return 0;
}


你可能感兴趣的:(POJ3352 Road Construction 双连通分量和桥 tarjan)