关于图的割点

在一个无向图中,若删除某一个节点,使得图分成若干个不相联通的子图,那么,这个节点就是图的割点。就像下图所示:

节点1、节点3和节点4,都是这个图的割点。为什么呢?你想啊,把节点1、节点3和节点4遮住,会怎么样?节点2永远也没法去到6 5 8 7 那些,同理,遮住节点4后,7,8也没法去到6 5 了。这样,就形成了一个个独立子图了。

那么,我们怎么知道这个点,就是割点呢?

①、用两个DFS,删除某个节点后,对其后面的节点进行DFS,如果在【不同过该节点】的情况下,不能回到以前的节点,那么,被删除的节点就是割点啦。删除?怎么删除,其实只需一个book数组,book[i]=1;DFS后,book[i]=0;就OK 啦。。起到一个临时标记,也就起到了"删除"的效果了。最后,book[i]=0;是要把删除的节点补回来。我们没理由弄破一个图吧?    ̄□ ̄||  最后,该算法的复杂度是O(N(N+M)); 怎样?很复杂吧?有没有其他优秀的算法呢?

②、只需一个DFS,不过要增加两个数组:

一个是num[];//记录的是这个顶点在遍历时,是第几个被访问到的

一个是low[];//表示一个节点,在不经过其父亲时,能回到最早的祖先是谁

::>_<:: 。。什么是父亲?祖先又是谁?这里我们把问题抽象化一下,在上图中,假设我们从1号顶点开始DFS,那么,1顶点能直接去到的顶点,我们称其为1号顶点的儿子,那么,一号顶点就是其父亲。这里需要记住一点:【父亲的num[i]】总是比儿子的少,因为辈分越大,数值越小,我们可以理解为,我爷爷是第一代传人,爸爸是第二代,我是第三代。记住这点很重要哦⊙▂⊙。

那么,假如一个顶点cur,在不经过其父亲顶点father的时候,总能回到更早的顶点(father的祖先),那么,这个father就不是割点。就比如图中的5号顶点,(根据上面,此时的father=5,cur=4),如果删除了5号顶点,cur还是能够通过6号顶点回到3顶点,甚至能回到1号顶点,此时,cur就能回到father的祖先了,此时,father=5并不是图的割点。

相反,如果一个顶点cur,在不经过其父亲顶点father的时候,总不能回到更早的顶点(father的祖先),那么,这个father就是割点啦!比如图中的4号顶点。

那么,我们怎么判断它不能回到自己【父亲的祖先呢】?其实只需要low[cur]>=num[father];就是,儿子能回到最早的祖先,最多最多,也只是它的爸爸。也就是,我不问过我爸爸,我连我爷爷是谁都不知道,那么,爸爸起到连通我和我爷爷的作用,所以,我爸爸就是割点了。 ̄□ ̄||。  我们再来形象理解下这句代码low[cur]>=num[father];我们用辈分来论事,记得我上面说过一句话很重要的吗?拉上去看看吧。这里,我先说个小故事。

话说,在古代,风家人才辈出,能才能武,祖祖祖祖祖祖祖祖爷也还在世上。 ̄□ ̄||。要知道,在这些高人辈出的家族里,有些长辈不是你想见就能见的,有些族人一生在世,没见过爷爷一面也有可能,因为高手一般都是闭关的。。但是如果经过父亲的推荐,那就不同了。一般儿子是绝对知道自己的父亲的,这点不用我说了吧。 ̄□ ̄||。所以cur的father就向他的父亲(就是cur的爷爷)说:我儿子cur天生神力,资质颇高,父亲你就点拔他一些武功吧,爷爷说,"哦,好好好,报名费5000大洋!"这样,cur就能知道他爷爷是谁了。但是,如果cur的父亲father不是伯乐,不知道cur是千里马,那就麻烦了,cur会被埋没的。就这样,cur一辈子平平淡淡,真是伤仲永哇,cur的父亲father,成了cur的"割点"了,就是,cur在不经过他爸爸father的推荐下,不能知道他的爷爷,爷爷爷,爷爷爷爷是谁,那么此时,cur只认识他的爸爸。说来说去,你还没告诉我low[cur]>=numer[father]是什么嘛!我该怎么判断cur是否只认识他爸爸呢?(请把这两句话看3遍)。没错,你想到了,low[cur]>=number[father];就是表明cur最多只认识他的爸爸。low[cur]==number[father]的时候,就表明cur知道自己的爸爸。为什么是?>=而不是<=呢?我们看看辈分?爷爷是第一辈,爸爸是第二辈,我是第三辈。如果我知道我爷爷是谁,low[cur]=1了,而number[father]=2;此时,low[cur]<number[father]。也就说明我能去到爷爷,father不是割点。

number[]//其实number代表的是自己的辈分,

low[];//代表我能认识到最老的长辈。注意,越老,值越小。因为辈分是1

说了那么多,我们来看看完整的代码?

#include <stdio.h>
#include <stdlib.h>
int n,m;//n代表有多少个节点,m代表有多少条边
//要注意m>=n-1最小也要有n-1条边才能把图接上
int e[51][51];//用邻接表来存一个图
int num[51],low[51];
int flag[51];//用来表示哪一个是割点
int root;//根节点。
int first;//表示一开始num和low的值
int min (int x,int y)
{
	return x>y?y:x;
}
void dfs (int cur,int father)
{
        int child=0;//代表cur有多少个儿子
        int i;//用来循环的而已
	first++;//这个我们称为时间戳
	num[cur] = first;//刚开始的辈分
	low[cur] = first;//刚开始能访问到的,只能是自己
	for (i=1;i<=n ;i++ )//循环一个图
	{
        if (e[cur][i]==1)//如果cur去i是有路的
        {
			if (num[i]==0)//就是i还没被访问过。先跳去看else
			{
				child++;//儿子数+1
				dfs (i,cur);//分清楚谁是爸爸,谁是儿子,此时很
				//明显cur是i的爸爸

				//结束dfs后
				low [cur] = min(low[cur],low[i]);//更新我和儿子能访问到的人
				//如果我儿子能访问到我太爷,但是我爸爸又不推荐我去见我太爷
				//那么,我可以通过儿子去见啊,父之命,能不从?

				if (cur != root && low[i]>=num[cur])
				//此时儿子是i 父亲是cur
				//如果我不是第一辈的人,并且,我的儿子是渣渣,失去我的推荐后
				//自己不能去到我的爸爸或者更早的人
				//那么,我成了我儿子的割点了
				//其实究竟是我伤仲永,还是我儿子本来就不是千里马呢?哈哈
				//有些东西,一个巴掌拍不响
				{
					flag[cur] = 1;//我是割点
				}
				if (cur == root &&child==2)
			    //如果我是第一辈,并且我有两个儿子
				{
					flag[cur]=1;
				}
			}
			else if (i!=father)//如果节点i被访问过
//但是,这个i不是cur的父亲father。我们动起笔,模拟一下这个过程
//首先(先不看1-->2)1-->3,到3的dfs,for循环中,第一个值i=1,就是
//3访问他父亲了,这是没意思的,但是我们又不能book标记它,这样会造成很多
//节点访问不到的,所以,如果访问到的节点是父亲,我们不做任何事情。
//就是上面else if不成立,但是,如果不是呢?就是6能去到3了,而6号的父亲是
//4啊。。那么,恭喜你,你拜见了你的爷爷爷 爷了,快去拜师吧
			{
				low[cur] = min(low[cur],num[i]);//拜师中
				//注意此时的cur是6,i是3
				//就是,cur去到的辈分的i的辈分
				//也就是num[i]。当然low[cur]比num[i]更小就另当别论了
				//也就是cur见到了太太太爷。。。哇靠!神人啊。
				//太爷出来见他
			}
        }
	}
}
void work()
{
	scanf ("%d%d",&n,&m);
	int i,j;
	for (i=1;i<=n ; i++)
	{
		for (j=1;j<=n ;j++ )
		{
			e[i][j] = 0;//初始化一个图
		}
	}
	for (i=1;i<=m ;i++ )
	{
		int u,v;
		scanf ("%d%d",&u,&v);
		e[u][v] = 1;//表示有路可去
		e[v][u] = 1;//无向图嘛
	}
	root=1;//先把root设置成1
	dfs (1,root);//从节点1开始,把节点1当成是根节点
	for (i=1;i<=n ;i++ )
	{
		if (flag[i])
		{
			printf ("%d ",i);//输出割点
		}
	}
	return ;
}
int main()
{
	work();//调用一个函数来分担主函数的代码
	return 0;
}

  对了,我还没说过child和root的事情,root代表的是祖爷(第一代传人,就是家主)。因为家主没可能成为其儿子的割点啊,家主都没父亲(他从石头爆出来的),那么1为什么还是割点啊,这里他是妨碍了自己的儿子见面,他的儿子也是的,师兄都不见面,各立一派吗?哦,对哦,家主之位,岂能乱传?所以,一般古代那些皇帝的儿子,都是闹不和的,为什么?大家都想自己的爸爸给皇帝位给我啊。!!!

child代表的是儿子,要注意,这里的child==2不代表家主只有一个儿子,代表的是,家主闹不和的儿子达到了两个,家主就是割点了。。最后,希望我们能动笔把每次dfs都写出来,这样,理解起来,更加的好、、

本人初出茅庐,有什么不正确的地方,希望朋友们能指教下,我感激不尽。。共同努力

时间: 2024-10-15 21:53:35

关于图的割点的相关文章

图的割点(邻接矩阵实现)

/* Name: 图的割点(邻接矩阵) Copyright: Author: 巧若拙 Date: 21-11-14 20:34 Description: 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合. 求割点与桥的算法是R.Tarjan发明的.对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号(等价于时间戳). 定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的节

图的割点、桥与双连通分支

原文地址:图的割点.桥与双连通分支 [点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合.一个图的点连通度的定义为,最小割点集合中的顶点数. 类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合.一个图的边连通度的定义为,最小割边集合中的边数. [双连通图.割点与桥] 如果一个无向连通图的点连通度大于1,则称该图是点双连通的(point biconne

图的割点(边表集实现)

/* Name: 图的割点(边表集实现) Copyright: Author: 巧若拙 Date: 20-11-14 21:17 Description: 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合. 求割点与桥的算法是R.Tarjan发明的.对图深度优先搜索,定义DFS(u)为u在搜索树(以下简称为树)中被遍历到的次序号(等价于时间戳). 定义Low(u)为u或u的子树中能通过非父子边追溯到的最早的

图的割点 | | jzoj【P1230】 | | gdoi | |备用交换机

写在前面:我真的不知道图的割点是什么.... 看见ftp图论专题里面有个dfnlow的一个文档,于是怀着好奇的心情打开了这个罪恶的word文档,,然后就开始漫长的P1230的征讨战.... 图的割点是这样的: path1=暴力 枚举每一个点,如果去掉这个点就可以使图断开,那么这个点就是割点(时间复杂度O(N(N+M))) path2=tarjan 从任意一个点开始遍历,记录遍历的顺序,然后对正在遍历的点进行一次深度优先遍历,但是此次遍历不允许经过这个点,看看还能不能回到前一个点 这时候我们就可以

【转载】图的割点、桥与双连通分支

[点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合.一个图的点连通度的定义为,最小割点集合中的顶点数. 类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合.一个图的边连通度的定义为,最小割边集合中的边数. [双连通图.割点与桥] 如果一个无向连通图的点连通度大于1,则称该图是点双连通的(point biconnected),简称双连通或重连通.一个

【转】图的割点、桥与双连通分支

原文地址:https://www.byvoid.com/blog/biconnect 图的割点.桥与双连通分支 [点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合.一个图的点连通度的定义为,最小割点集合中的顶点数. 类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合.一个图的边连通度的定义为,最小割边集合中的边数. [双连通图.割点与桥] 如果一个

Tarjan算法:求解图的割点与桥(割边)

简介: 割边和割点的定义仅限于无向图中.我们可以通过定义以蛮力方式求解出无向图的所有割点和割边,但这样的求解方式效率低.Tarjan提出了一种快速求解的方式,通过一次DFS就求解出图中所有的割点和割边. 欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. 割点与桥(割边)的定义 在无向图中才有割边和割点的定义 割点:无向连通图中,去掉一个顶点及和它相邻的所有边,图中的连通分量数增加,则该顶点称为割点. 桥(割边):无向联通图中,去

图的割点算法、图的割边算法

割点算法 • 在一个无向连通图中,如果删除某个顶点后,图不再连通(即任意两点之间不能相互到达),我们称这样的顶点为割点(或者称割顶). 判断一个顶点是不是割点除了从定义,还可以从DFS(深度优先遍历)的角度出发.我们先通过DFS定义两个概念. 假设DFS中我们从顶点U访问到了顶点V(此时顶点V还未被访问过),那么我们称顶点U为顶点V的父顶点,V为U的孩子顶点.在顶点U之前被访问过的顶点,我们就称之为U的祖先顶点. 显然如果顶点U的所有孩子顶点可以不通过父顶点U而访问到U的祖先顶点,那么说明此时去

图的割点与割边(超详细!!!)

·割点 割点概念,应该很好理解: 在一个无向图中,如果删除某个顶点,这个图就不再连通(任意两点之间无法相互到达),那么这个顶点就是这个图的割点. 举个例子: 图中的2号顶点就是割点, 删除2号后,4,5不通,1,6也不通等等 如何求割点? 很容易想到的方法是:依次删除每一个顶点,然后用dfs或者bfs来检查图是否依然连通.如果删除某个顶点后,导致图不再连通,那么刚才删除的顶点就是割点. 这种方法的时间复杂度是O(N(N+M)). 下面寻找复杂度低的方法来解决. 首先从图中任意节点开始dfs遍历上