无向图的双连通分量

news/2024/7/24 2:11:35

边双连通分量

/*
无向图的边双连通分量(e-DCC)及缩点 
若一张无向图不存在桥,则称这张图为边双连通图
极大边双连通图称为边双连通分量
求法:
用链式前向星存图方便标记边 
找出原图中所有的桥,将桥去掉后形成的各个连通块
即为边双连通分量 
最后缩点就把双连通分量缩为一个点即可 
*/

#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 5e4 + 5;

struct edges{
	int to,next; 
}edge[600005];

int cntx = 0;
int head[maxn];

void add(int x,int y)
{
	edge[cntx].to = y;
	edge[cntx].next = head[x];
	head[x] = cntx++;
}

int dfn[maxn],low[maxn];
bool bridge[600005];
int dcc[maxn],cnt = 0,c = 0;  //dcc[i]表示i在第几个边双连通图中 

void tarjan(int x,int in_edge)   //判桥  
{
	dfn[x] = low[x] = cnt ++;
	for (int i = head[x]; i != -1; i = edge[i].next)
	{
		int t = edge[i].to;
		if( !dfn[t] )   
		{
			tarjan(t,i);
			low[x] = min(low[x],low[t]);    
			if( low[t] > dfn[x] )  
			{
				bridge[i] = bridge[i^1] = true;  
			} 
		}else if( i != ( in_edge ^ 1 ) ) low[x] = min(low[x],dfn[t]); 	 
	}
}

void dfs(int x)    //搜索来判断每个点所在的边双连通图 
{
	dcc[x] = c;
	for (int i = head[x]; i != -1; i = edge[i].next)
	{
		int y = edge[i].to;
		if( dcc[y] || bridge[i] ) continue;    //跳过桥
		dfs(y); 
	}
} 

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;
	cin >> n >> m;
	memset(head,-1,sizeof(head));
	for (int i = 1; i <= m; i++)
	{
		int x,y;
		cin >> x >> y;
		if( x == y ) continue; 
		add(x,y),add(y,x);
	} 
	for (int i = 1; i <= n; i++)
	{
		if( !dfn[i] ) tarjan(i,i);
	}
	for (int i = 1; i <= n; i++)
	{
		if( !dcc[i] )
		{
			c ++;
			dfs(i);
		}
	}
	cout << c << '\n';
 	return 0;
}

点双连通分量

/*
无向图的点双连通分量
注意: 
孤立点也算一个双连通分量
一个割点可能在多个双连通分量中
计算过程:
在tarjan过程中维护一个栈
1.当一个节点第一次被访问时,把该节点入栈
2.当发现这个点x为割点时,无论是否为根,都要执行以下操作
(1)从栈顶不断弹出节点,直至y节点(x的儿子节点)被弹出
(2)弹出的所有节点与x节点一起构成一个v-DCC 
*/ 

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

const int maxn = 5e4 + 5;

//由于割点可能出现在多个点强连通分量中,所以我们不能按原来的方式标记每个点 
vector<int> g[maxn],dcc[maxn];  //存点强连通分量 
int cnt = 1,top = 0,c = 0;
int dfn[maxn],low[maxn],sta[maxn]; 
int check[maxn];   

void tarjan(int x,int fa)    //当前节点和连通块的根节点 
{
	dfn[x] = low[x] = cnt ++;
	sta[++top] = x;   //x入栈
	if( x == fa && g[x].size() == 0 )  //孤立点 
	{
		dcc[++c].push_back(x);
		return;
	}
	int child = 0;
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i];
		if( !dfn[t] )    //如果这个点之前没被访问,即儿子节点 
		{
			tarjan(t,fa);
			low[x] = min(low[x],low[t]);    //t节点能到的x一定能通过t节点到达 
			if( low[t] >= dfn[x] )  //儿子节点无法通过别的边到达比x小的点,那么说明x为割点 
			{
				child ++; 
				if( x != fa || child > 1 ) check[x] = 1;
				c ++;
				int z;   //找到满足的,执行以下操作,出栈直到t出栈 
				do {
					z = sta[top--];
					dcc[c].push_back(z);
				}while( z != t );
				dcc[c].push_back(x);
			} 
		}
		low[x] = min(low[x],dfn[t]); 
		 //x可以到t,但是不可以由t再到达别的点
		 //若t为x的父节点,并不影响结果,因为有等于。
		 //若t不为x的父节点,那么t一定为x的祖先节点
		 //那么就无法根据x判断其父节点是否为割点,更新到祖先节点即可		 
	}
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int n,m;
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int x,y;
		cin >> x >> y;
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for (int i = 1; i <= n; i++)
	{
		if( !dfn[i] ) tarjan(i,i);
	}
	for (int i = 1; i <= c; i++)
	{
		for (int j = 0; j < dcc[i].size(); j++)
		{
			cout << dcc[i][j];
			if( j == dcc[i].size() - 1 ) cout << '\n';
			else cout << ' ';
		}
	} 
	return 0;
}

点双连通分量的缩点

/*
点双连通分量的缩点
由于一个割点属于多个v-dcc
所以我们把每个v-dcc和每个割点都作为新图的节点
并在每个割点与包含它的所有v-dcc之间连边 
*/

#include <iostream>
using namespace std;

int new_id[maxn];
vector<int> new_g[maxn];

void compress(int n)
{
	int num = c;
	for (int i = 1; i <= n; i++)
	{
		if( check[i] ) new_id[i] = ++num;  //给割点新的编号 
	}
	for (int i = 1; i <= c; i++)   //遍历点双连通块 
	{ 
		for (int j = 0; j < dcc[i].size(); j++)  
		{
			int x = dcc[i][j];
			if( check[x] )    //如果x是割点 
			{
				new_g[new_id[x]].push_back(i);  //割点与包含其的连通块相连 
				new_g[i].push_back(new_id[x]);
			}
		}
	}
} 

http://www.niftyadmin.cn/n/1037058.html

相关文章

object C 数据类型

Objective-C数据类型可以分为&#xff1a;基本数据类型、对象类型和id类型。基本数据类型有&#xff1a;int、float、double和char类型。对象类型就是类或协议所声明的指针类型&#xff0c;例如&#xff1a;NSAutoreleasePool * pool&#xff0c;其中NSAutoreleasePool是一个类…

java Math概念以常用方法

Math是一个在java lang下的类 他是一个最终类 因此没有子类 Math中包含了很多常用的数字方法 Math常用方法如下 这里我们一一演示 public static void main(String args[]) {System.out.println(Math.abs(7));System.out.println(Math.abs(-7));}简单说 abs就是负转正 正数则…

我的appstore新游戏--LeBallon 拿码了

LeBallon下载地址&#xff1a;http://itunes.apple.com/us/app/leballon/id481655887?mt8 推广码&#xff1a;P74AYPXA3RWY XEAA7797YPXH EAML94AJ4AEE HRHKKYXFN4F4 YFNKEJTL4KEL LeBallon是一个虚拟气球游戏&#xff0c;我们在手机上给你一个虚拟的气球&…

斯坦那树

/* 斯坦那树:一个无向图,n个点m条边,有k个关键点,求联通这k个点的最小代价 由于最后的结果一定是一棵树,所以dp[i][s]表示以i为根, 当前连通状态为s的代价,注意每个点都可能为根. dp[i][s]可以由s的子状态转移过来,也可以由别的点j通过i,j这条边与i相连形成以i为根的s 即dp[i]…

iPhone与iPad开发实战——精通iOS开发--视频

iPhone与iPad开发实战——精通iOS开发地址&#xff1a;http://v.51work6.com/courseInfoRedirect.do?actioncourseInfo&courseId240566 课程要求&#xff1a;熟悉C&#xff0c;C&#xff0c;objective C项目平台&#xff1a;演示&#xff1a;mac os版本&#xff1a;xcode3…

java System

System是java lang包下的一个最终类 继承了 Object类 System的常用方法就两个 我们来一一演示 我们先随便建一个类 参考代码如下 public static void main(String args[]) {System.out.println("开始");System.out.println("结束");}运行结果如下 但当我…

如何在Visual Studio 2010旗舰版本下安装Window Phone 7 简体中文开发环境

微软官方提供的Window Phone 7 开发工具包是VisualStudio 2010 Express for Window Phone7 (学习版或快捷版)&#xff0c;使用该版本有个问题是&#xff0c;不能打开传统的Visual Studio工程&#xff08;如&#xff1a;WinForm、WebServer、WebForm等&#xff09;&#xff0c;如…

动态dp

/* 动态dp,支持修改操作的dp最大值最小值维护 定义广义矩阵乘法 c[i][j]max(a[i][k]b[k][j]) 加法对min与max有结合律 对于转移g[i]max(g[i-1],f[i]),f[i]max(f[i-1]a[i],a[i]) 尝试构造矩阵,利用线段树来维护区间的广义矩阵乘法的值,这样就能算区间的dp值 */#include <i…