天天看点

无向图的边双连通分量

无向图边双连通分量:对于一个无向图,任意两点之间至少存在两条边不重复的路径,则图是双连通的;边双连通的极大子图为边双连通分量;

求解边双连通分量:

1、dfs找出所有的桥;

2、把桥边删除,原图形成多个连通块,每个连通块就是一个边双连通分量;(只需再次dfs不经过桥就行);

poj3352 求边双连通分量;

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<stack>
#include<string>
#include<map>
#define ll long long
#define MAX 1010
#define INF INT_MAX
#define eps 1e-8

using namespace std;

struct Edge{
	int from,to;
};

vector<int>G[MAX];
vector<Edge>edges;

int pre[MAX],low[MAX],iscut[MAX][MAX],c[MAX],dfs_clock,n;

void init(){
	for (int i = 0; i<=n; i++) G[i].clear();
	edges.clear();
	memset(iscut,0,sizeof(iscut));
	memset(pre,0,sizeof(pre));
	memset(c,0,sizeof(c));
	dfs_clock = 0;
}

int dfs(int u, int fa){
	int lowu = pre[u] = ++dfs_clock;
	for (int i=0; i<G[u].size(); i++){
		int v = G[u][i];
		if (!pre[v]){
			int lowv = dfs(v,u);
			lowu = min(lowu,lowv);
			if (lowv > pre[u]){       //标记桥
				iscut[u][v]  = iscut[v][u] = 1;
				edges.push_back((Edge){u,v});
			}
		}
		else if (pre[v] < pre[u] && v != fa){
			lowu = min(lowu,pre[v]);
		}
	}
	low[u] = lowu;
	return lowu;
}

int bcc[MAX],cnt;

void dfs1(int u){       //求边双连通分量
	if (bcc[u]) return;
	bcc[u] = cnt;
	for(int i=0; i<G[u].size(); i++){
		int v = G[u][i];
		if (iscut[u][v]) continue;
		dfs1(v);
	}
}

int main(){
	int m;
	while (scanf("%d%d",&n,&m) != EOF){
		int u,v;
		init();
		for (int i=0; i<m; i++){
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		dfs(1,-1);
	//	for (int i=0; i<edges.size(); i++) printf("%d  %d\n",edges[i].from,edges[i].to);
		memset(bcc,0,sizeof(bcc));
		cnt = 0;
		for (int i=1; i<=n; i++){
			if (!bcc[i]){
				cnt++;
				dfs1(i);
			}
		}
	//	printf("\n");
	//	for (int i=1; i<=n; i++) printf("%d ",bcc[i]); printf("\n");
		for (int i=0; i<edges.size(); i++){
			int x = edges[i].from;
			int y = edges[i].to;
			c[bcc[x]]++;
			c[bcc[y]]++;
		}
		int ans = 0;
		for (int i=1; i<=cnt; i++) if (c[i] == 1) ans++;
		printf("%d\n",(ans+1) / 2);
	}
	return 0;
}


           
这个题需要用到的命题:
           

一颗树有n(n>=2)个叶子节点的树,至少(只需)添加(n+1)/2条边,才(就)能转化为没有桥的图。或者说,使得图中 的每条边都至少在一个环上;