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

来源:互联网 发布:2018pscc是什么软件 编辑:程序博客网 时间:2024/05/29 07:25

题意

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