题意
- 无向图找出每个割点,然后求出删除这个割点所得的连通分量个数
- 节点编号在1-1000,但没说按顺序给出
思路
- 无向图求所有割点是一类经典问题,这篇blog就以这题为例简单介绍一下求解的算法思路
- 我们希望在O(n+m)的时间里求出所有的割点
- 首先,总体思路是对图进行dfs操作,dfs的过程其实对应了一棵dfs搜索树,而我们就利用这棵搜索树的独特性质,来解决求割点的问题
- dfs过程是,当我们搜索到节点u时,会遍历和u有边链接的所有节点v,这里有两种情况,一是v已经被搜索过了,二是v还没有被搜索过
- 对于第二种情况,我们自然会继续dfs(v)
- 而对于第一种情况,在无向图中,存在一个很好的性质。即v节点一定是dfs树中,u的一个祖先节点(或者u本身,这个情况可以通过把父亲节点id作为参数传给dfs避免掉,详细参见代码)。这个性质通过画个事例,然后反证法,很容易证明。这种情况中的边(u, v),我们称之为反向边
- 我们根据这个性质,可以得到一个引理1:若u是dfs树中的非根节点,则u是割点,当且仅当,dfs树中存在一个u的孩子节点v,以v为根的子树中,任意节点都没有连到u的祖先节点的反向边
- 而当u是根节点时,则u是割点的充要条件是,在dfs树中u的孩子节点数超过1
- 这个证明根据之前我们说的性质,和割点的定义不难推出
- 具体实现的时候,我们通过维护两个数组pre和low来实现
- pre记录一个时间戳,即pre[u]记录u是第几个被访问的节点
- low[u]记录以u为子树的节点中,反向边连到最早被访问的节点的pre值
- 具体到这个题里,要求删除节点u后的联通分量个数,只要统计一下满足引理1的节点v的个数即可知道
- 注意:节点编号不是连续排布以及图可能不连通的问题
实现
#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#include <set>
#include <cstring>
using namespace std;
const int maxn = 1e3+5;
map<int, set<int> > g;
map<int, int> pre;
map<int, int> low;
int count = 0;
map<int, int> cutNum;
int dfs(int u, int fa){
low[u] = pre[u] = ++count;
int child = 0;
for (set<int>::iterator it = g[u].begin(); it != g[u].end(); it++){
int v = *it;
if (fa == v){
continue;
}
if (!pre[v]){
child++;
low[u] = min(low[u], dfs(v, u));
if (low[v] >= pre[u]){
cutNum[u]++;
}
}
else{
low[u] = min(low[u], pre[v]);
}
}
if (fa == -1 && child == 1){
cutNum.erase(cutNum.lower_bound(u));
}
else if (fa != -1 && cutNum.find(u) != cutNum.end()){
cutNum[u]++;
}
return low[u];
}
int main(){
for (int t=1;true;t++){
int u, v;
g.clear();
pre.clear();
cutNum.clear();
low.clear();
count = 0;
while (scanf("%d", &u) != EOF && u){
scanf("%d", &v);
g[u].insert(v);
g[v].insert(u);
}
if (!g.size()){
break;
}
for (map<int, set<int> >::iterator it = g.begin(); it != g.end(); it++){
int u = it->first;
if (!pre[u]){
dfs(u, -1);
}
}
printf("Network #%d\n", t);
if (!cutNum.size()){
puts(" No SPF nodes");
}
for (map<int, int>::iterator it = cutNum.begin(); it!=cutNum.end();it++){
int u = it->first;
int num = it->second;
printf(" SPF node %d leaves %d subnets\n", u, num);
}
puts("");
}
return 0;
}