图的联通性

图的联通性

0.【前置知识】 图上dfs相关概念

vis数组:在图的遍历中,往往设置了一个标记数组vis的bool值来记录顶点是否被访问过。但有些时候需要改变vis值的意义。令vis具有3种值并表示3种不同含义

vis = 0,表示该顶点没没有被访问

vis = 1,表示该顶点已经被访问,但其子孙后代还没被访问完,也就没从该点返回

vis = 2,表示该顶点已经被访问,其子孙后代也已经访问完,也已经从该顶点返回

可以vis的3种值表示的是一种顺序关系和时间关系。

DFS过程中,对于一条边u->v

vis[v] = 0,说明v还没被访问,v是首次被发现,u->v是一条树边又称树枝边

vis[v] = 1,说明v已经被访问,但其子孙后代还没有被访问完(正在访问中),而u又指向v?说明u就是v的子孙后代,u->v是一条后向边,因此后向边又称返祖边

vis[v] = 2,说明v已经被访问,其子孙后代也已经全部访问完,u->v这条边可能是一条横叉边,或者前向边

1.无向图的联通性

联通:两点存在一条连接它们的路径。

联通块:里面的点两两联通。

桥:对于一个联通无向图,一条边是桥当且仅当去掉这条边后图变得不连通。

强连通分量:没有桥的联通块。

(无向图的强连通分量也叫边双联通分量,对应的概念是:

点双连通:没有割点的双连通分量

对于特定的题目会有求双连通分量的操作。)

​ tarjan算法判桥:

​ dfn[x]是dfs序,代表x是第几个搜到的。

​ instack[x]表示当前的点是否在dfs栈中,是用来判断是否是返祖边,代码中if(instack[y])即代表x点在y的子树中。

​ 不过无向图联通分量中instack数组是不必要的。

由于无向图中都是双向边,前向边的情况对不更新low(tarjan中用if(instack[y])将前向边特判掉不更新)没有意义(证明:如果存在一条前向边u->v,则亦存在v->u,所以在dfs到v时已经更新了v。再dfs到v时,即使不更新也没意义了)所以

​ low[x] PPT:x通过非返祖边,且至多通过一条非树边能到达的最小dfn。

​ 我的理解:x至多通过一条非树边(即不是返回父亲的返祖边)能到达的最小dfn。

​ 感觉洪老师把返祖边的定义弄错(混)了:一方面字面上以为是返回父亲的边,另一方面代码里用的是instack正确的判断,导致没有判真正返回父亲的边。

void tarjan(int x,int fa) {
	instack[x] = 1;
	dfn[x] = low[x] = ++tot;
	for (auto y : E[x]) {
		if (!dfn[y]) {
			tarjan(y,x);
			low[x] = min(low[x], low[y]);
		}
		else {
			if(y!=fa&&instack[y])low[x] = min(low[x], dfn[y]);
		}
	}
	instack[x] = 0;
}
//-----ppt中的错误代码,没有特判掉指向父亲的返祖边--------------
void tarjan(int x) {
	instack[x] = 1;
	dfn[x] = low[x] = ++tot;
	for (auto y : E[x]) {
		if (!dfn[y]) {//树边
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else {
			if(instack[y])low[x] = min(low[x], dfn[y]);//返祖边,一般指向父亲
		}
	}
	instack[x] = 0;
}

判桥: low[x]<dfn[x]代表存在一条返回到父亲之上某点y的返祖边,y有一条路径到x,x有另一条路径到y。(形成双连通分量??)

反之low[x]>=dfn[x]代表它连向父亲的边为桥

2.有向图的联通性

强连通分量:任意两点都可以互相到达。

在做 Tarjan算法时,如果 tarjan(x) 后发现 dfn[x]==low[x],则 x 的⼦树⾥的剩下的所有点构成⼀个强连通分量,可以用一个栈来维护:

stack<int> S;
void tarjan(int x) {
	S.push(x);
	instack[x] = 1;
	dfn[x] = low[x] = ++tot;
	for (auto y : E[x]) {
		if (!dfn[y]) {
			tarjan(y, x);
			low[x] = min(low[x], low[y]);
		}
		else {
			if ( instack[y])low[x] = min(low[x], dfn[y]);
		}
	}
	if (low[x] == dfn[x]) {
		while (1) {
			int now = S.top();
			S.pop();
			instack[x] = 0;//把之前没有清零的一次性更新。
			/*
				染色,统计之类的操作
			*/
			if (now == x)break;
		}
	}
	
}

3.例题

0.

题意:

有N个⼈,给你M对整数 (a,b),表示第 a 个⼈认为 b 很厉 害,⽽这种关系具备传递性,也就是如果 a 认为 b 厉害, 且 b 认为 c 厉害,则 a 认为 c 厉害

求有多少⼈被所有⼈都觉得很厉害

N,M<=10^5

做法:

无环:有向图无环 -> 至少有一个点没有出边。于是任意找一个点,一直走到没有出边的点,判一下是否所有点都连向它。

有环:对于这样一个环,必为强连通,缩点后变成无环情况(会多出重边)。

套路:一个题在没有环(是一个拓扑图)的时候很好做,就考虑缩环。

1.POJ1236

题意:

给定一个有向图,N个点,求:

1)至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点

2)至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

思路

按照套路,Tarjan算法求SCC,并缩点建图。那么对于问题1,新的图中入度为0点的点数即是答案。

对于问题2,答案为max(入度为0的点的点数,出度为0的点数),因为对于每个入度或出度为0的点,需要连一条边来解决,那么将出度为0的点连向入度为0的点是最优的。

此外,如果最后SCC只有1个,那么问题2的答案应该特判为0。

套路:有向图构造联通分量的方法:将出度为0的点连到入度为0的点上有向图。

​ 无向图构造联通分量的方法:将度为1的点(叶子)相互连接。

2.POJ3177

题意:有n个牧场,Bessie 要从一个牧场到另一个牧场,要求至少要有2条独立的路可以走。现已有m条路,求至少要新建多少条路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指:没有公共边的路,但可以经过同一个中间顶点。给出的图保证已经联通。

思路:

在同一个边双连通分量(即无向边的强连通分量)中,任意两点都有至少两条独立路可达,所以同一个边双连通分量里的所有点可以看做同一个点。

套路缩点后,新图是一棵树,因为原图已联通。现在就是要在树上添边,令所有点缩为一个边双联通分量。

再套路连叶子,答案就是(树上度为1的点数+1)/2。

3.HDU3394

题意:有一个公园有n个景点,公园的管理员准备修建m条道路,并且安排一些形成回路的参观路线。如果一条道路被多条道路公用,那么这条路是冲突的;如果一条道路没在任何一个回路内,那么这条路是不冲突的。问分别有多少条有冲突的路和没有冲突的路

思路:

首先非多余的边很明显是桥,因为其不在任何一个回路中。

那么冲突边是多个回路共用的边,注意此处的回路是简单回路,那么考虑点BCC缩点建图,如果一个BCC里的边数大于点数,那么其至少2个环,其中的所有边的都是冲突边。如果点数等于边数,那么只有一个大环,没有冲突变,而点BCC中不存在点数少于边数的情况。

posted @ 2019-07-25 22:11  SuuTTT  阅读(535)  评论(0编辑  收藏  举报