dfs的深入挖掘应该是分为几个部分,今天学习了大概三个部分
第一:连通分量 ( 并查集也能做 )
判断一个图有几个连通分量,并且为每个点标号---说明它是属于哪一个连通分量的
那么dfs函数可以写成如下:
void dfs(int u ) {
for ( int i = 1; i <= n; ++i ) if ( !vis[i] && map[u][i]) {
cc[i] = count; //count为连通分量的标号,cc【i】记录i所属的连通分量的标号
vis[i] = true;
dfs(i);
}
return;
}
第二:判断是否为二分图
和上一个dfs的代码很像,什么是二分图,之前也上一篇文章里提到了。
就是给所有的节点进行涂色,二分图有两组节点,一组我们图成1,另一组图成2(1和2分别代表一种颜色), 没有涂色的点,我们用0来标志
int color【MAX】;
color[0] = 1; //先给这个点着色,并且以它作为dfs的起点
bool bipa( int u ) {
for ( int i = 0; i < n; ++i ) {
if ( map[u][i] ) {
if ( color[u] == color[i] ) return false; // 说明有两个相连的点是一个颜色的
if ( !color[i] ) color[i] = 3 - color[u];
if ( !bipa(i) ) return false;
}
}
return true;
}
第三:判断割顶
这里面有一个比较关键的概念,就是时间戳。
怎么求时间戳呢?时间戳就是在按照一点顺序,一般都是标号大小的顺,对连通分量进行dfs的遍历,然后对每个结点,求出一个咋dfs中的相对的开始时间和截止时间,用pre这个数组表示开始涉及到这个点的时候,用post表示dfs在递归到这个点之后执行完毕的时间,也就是说跳出这层递归的时间,一看代码就会明白了
int pre[MAX], post[MAX];
void dfs( int u ) {
pre[u] = level++; //level可以被看做是相对的时间点,起始时间为0
for ( int i = 0; i < n; ++i ) {
if ( !vis[i] && map[u][i] ) {
vis[i] = true;
dfs(i) ;
}
}
post[u] = level++;
}
根据dfs遍历节点的顺序,可以画出一棵dfs的树
对于割顶,有这样一个定理:
第一,如果是树根的话,当且仅当它有跟多的自己点
第二,如果不是树根的话,设任意节点u, 当且仅当存在u的一个子节点v,使得v节点及其所有后代都没有反向边连回u的祖先,但是可以连回u
(定理的参考文献是《算法入门经典——训练之南》)
割顶这部分今天晚上继续研究。