无向图求割点

http://blog.csdn.net/minjie_mj/archive/2010/07/13/5729843.aspx

通俗来说,割点就是在一个无向图中,去掉这个点以及与它关联的边,原来连通的图变为了若干不连通的块的一个点。求割点常见的算法就是DFS,在DFS时记录每个节点的深度dep和它的子孙所能达到的最浅深度low。然后对于每一个节点:

1) 如果u该点是根节点并且有两个或者两个以上儿子,那么u是一个割点

2) 如果u不是根节点并且存在它的一个儿子v,使得low[v] >= dep[u],那么u是割点

伪代码如下:


图割点的题有很多。这里列出TOJ 1026,TOJ2189,TOJ2299

 

TOJ1026   http://acm.tju.edu.cn/toj/showp1026.html

  1. /* DFS求割点,在DFS时记录每个节点的深度dep和它的子孙所能达到的最浅位置low  
  2.  * 1)如果u根节点儿子大于1个,根节点为割点  
  3.  * 2)如果u不是根节点且对于u的子孙v有low[v]>=dep[u],u为一个割点  
  4.  * 注意输入和输出(输入用getchar() != '/n' 来控制一行 )  
  5.  */   
  6. #include <cstdio>   
  7. #include <cstring>   
  8. #define M 102   
  9. using   namespace  std;  
  10. int  map[M][M],low[M],dep[M];  
  11. int  f[M],n,depth,root;  
  12. bool  flag[M];  
  13. int  min( int  a, int  b){  
  14.     return  a<b?a:b;  
  15. }  
  16. void  dfs( int  m){  
  17.     dep[m] = depth;  
  18.     low[m] = depth;  
  19.     depth++;  
  20.     flag[m] = true ;  
  21.     int  i,j,k;  
  22.     for (i = 1;i <= n; i++){  
  23.         if (map[m][i]){  
  24.             if (!flag[i]){  
  25.                 dfs(i);  
  26.                 low[m] = min(low[m],low[i]);  
  27.                 if (low[i] >= dep[m] && m != 1)  
  28.                     f[m] ++;  
  29.                 else   if (m == 1) root ++;  
  30.             }  
  31.             else     //有后向边   
  32.                 low[m] = min(low[m],dep[i]);  
  33.         }  
  34.     }  
  35. }  
  36. int  main()  
  37. {  
  38.     int  i,j,k;  
  39.     while (scanf( "%d" ,&n),n){  
  40.         memset(map,0,sizeof (map));  
  41.         memset(f,0,sizeof (f));  
  42.         memset(low,0,sizeof (low));  
  43.         memset(flag,false , sizeof (flag));  
  44.         memset(dep,0,sizeof (dep));  
  45.         root  = 0;  
  46.         while (scanf( "%d" ,&k),k){  
  47.             while (getchar() !=  '/n' ){  
  48.                 scanf("%d" ,&j);  
  49.                 map[k][j] = map[j][k] = 1;  
  50.             }  
  51.         }  
  52.         root = 0;  
  53.         depth = 1;  
  54.         dfs(1);  
  55.         int  ans = 0;  
  56.         if (root > 1) ans ++;  
  57.         for (i = 2;i <= n; i++)  
  58.             if (f[i]) ans ++;  
  59.         printf("%d/n" ,ans);  
  60.     }  
  61. }  

TOJ2189   http://acm.tju.edu.cn/toj/showp2189.html

  1. /* DFS求割点,在DFS时记录每个节点的深度dep和它的子孙所能达到的最浅位置low  
  2.  * 1)如果u根节点儿子大于1个,根节点为割点  
  3.  * 2)如果u不是根节点且对于u的子孙v有low[v]>=dep[u],u为一个割点  
  4.  * 注意输入和输出(输入用getchar() != '/n' 来控制一行 )  
  5.  */   
  6. #include <cstdio>   
  7. #include <cstring>   
  8. #include <algorithm>   
  9. #define M 1002   
  10. using   namespace  std;  
  11. int  low[M],dep[M],f[M];  
  12. bool  flag[M],map[M][M];  
  13. int  n,depth,root,cont;  
  14. int  min( int  a, int  b){  
  15.     return  a<b?a:b;  
  16. }  
  17. void  dfs( int  m){  
  18.     dep[m] = depth;  
  19.     low[m] = depth;  
  20.     flag[m] = true ;  
  21.     depth++;  
  22.     int  i,j;  
  23.     for (i = 1;i <= n; i++){  
  24.         if (map[m][i]){  
  25.             if (!flag[i]){  
  26.                 dfs(i);  
  27.                 low[m] = min(low[m],low[i]);  
  28.                 if (low[i] >= dep[m] && m != 1)  
  29.                     f[m] ++;  
  30.                 else   if (m == 1)  
  31.                     root ++;  
  32.             }  
  33.             else   
  34.                 low[m] = min(low[m],dep[i]);  
  35.         }  
  36.     }  
  37. }  
  38. int  main()  
  39. {  
  40.     int  i,j,k;  
  41.     while (scanf( "%d" ,&n),n){  
  42.         memset(map,false , sizeof (map));  
  43.         memset(f,0,sizeof (f));  
  44.         memset(low,0,sizeof (low));  
  45.         memset(flag,false , sizeof (flag));  
  46.         while (scanf( "%d" ,&k),k){  
  47.             while (getchar() !=  '/n' ){  
  48.                 scanf("%d" ,&j);  
  49.                 map[k][j] = map[j][k] = true ;  
  50.             }  
  51.         }  
  52.         depth = 1; root = 0; cont = 0;  
  53.         dfs(1);  
  54.         int  ans = 0;  
  55.         if (root > 1){  
  56.             ans ++;  
  57.             f[1]= 1;  
  58.         }  
  59.         for (i = 2;i <= n; i++)  
  60.             if (f[i]) ans ++;  
  61.         printf("%d" ,ans);  
  62.         if (ans > 0){  
  63.             int  tep = 0; printf( " " );  
  64.             for (i = 1;i <= n; i++)  
  65.                 if (f[i]){  
  66.                     tep ++;  
  67.                     printf("%d" ,i);  
  68.                     if (tep != ans) printf( " " );  
  69.                     else  printf( "/n" );  
  70.                 }  
  71.         }  
  72.         else  printf( "/n" );  
  73.     }  
  74. }  

TOJ2299  http://acm.tju.edu.cn/toj/showp2299.html

  1. /* 给出一个无向图,计算去掉一个点后所能得到的最大块数  
  2.  * 1)每次DFS时num++,表示图的块数,然后在DFS过程中求出割点并且存下去掉割点连通分量增加的块数,  
  3.  *   注意根节点的f[root]最后要-1,因为1个儿子的根节点不会增加图的块数  
  4.  * 2)找到最大的f[i]和num相加即为所求,但是注意考虑特殊情况,零图  
  5.  *  
  6.  */   
  7. #include <cstdio>   
  8. #include <cstring>   
  9. #include <vector>   
  10. #define MAXN 10002   
  11. using   namespace  std;  
  12. int  f[MAXN],low[MAXN],dep[MAXN];  
  13. bool  flag[MAXN];  
  14. int  n,depth,root,father,num,son;  
  15. vector<int >mapp[MAXN];  
  16. void  DFS_CCS( int  m){  
  17.     int  i,u;  
  18.     low[m] = dep[m] = depth++;  
  19.     flag[m] = true ;  
  20.     for (i = 0;i < mapp[m].size(); i++){  
  21.         u = mapp[m][i];  
  22.         if (!flag[u]){  
  23.             DFS_CCS(u);  
  24.             if (low[u] >= dep[m]) f[m] ++;  
  25.             if (low[m] > low[u]) low[m] = low[u];  
  26.         }  
  27.         else   if (low[m] > dep[u]) low[m] = dep[u];  
  28.               
  29.     }  
  30. }  
  31. void  cal_ccs(){  
  32.     int  i,j;  
  33.     memset(flag,false , sizeof (flag));  
  34.     memset(low,0,sizeof (low));  
  35.     memset(f,0,sizeof (f));  
  36.     memset(dep,0,sizeof (dep));  
  37.     depth = 0; num = 0;   
  38.     for (i = 0;i < n; i++)  
  39.         if (!flag[i]){  
  40.             num ++;   
  41.             son = 0;  
  42.             father = i;  
  43.             DFS_CCS(i);  
  44.             if (f[father] != 0) f[father]--;  
  45.         }  
  46. }  
  47. int  main()  
  48. {  
  49.     int  i,j,k,m;  
  50.     while (scanf( "%d%d" ,&n,&m)){  
  51.         if (m == 0 && n == 0) break ;  
  52.         for (i = 0;i < n; i++) mapp[i].clear();  
  53.         while (m--){  
  54.             scanf("%d%d" ,&i,&j);  
  55.             mapp[i].push_back(j);  
  56.             mapp[j].push_back(i);  
  57.         }  
  58.         cal_ccs();  
  59.         if (num == n) printf( "%d/n" ,n-1);           //注意考虑零图的情况   
  60.         else {   
  61.             int  mm = 0;  
  62.             for (i = 0;i < n; i++)  
  63.                 if (f[i] > mm)  
  64.                     mm = f[i];  
  65.             printf("%d/n" ,mm + num);  
  66.         }  
  67.     }  

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值