poj 1523 求无向图所有割点以及删除割点后连通分量个数 给出详细算法思路

题意

  • 无向图找出每个割点,然后求出删除这个割点所得的连通分量个数
  • 节点编号在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;
}
点赞