POJ 1523 SPF (割点,连通分量)
题意:给出一个网络(不一定连通),求所有的割点,以及割点可以切分出多少个连通分量。
思路:很多种情况。
(1)如果给的图已经不是连通图,直接“ No SPF nodes”。
(2)求所有割点应该不难,就是tarjan发明的算法搞定。但是求连通分量就得小心了,多种情况。看下:
1)如果一个割点x,其所有孩子都不是割点,那么x至少可以割出两个连通分量(x之上和之下的各1个)。
2)如果一个割点x,其有部分孩子是割点,有部分孩子并不是割点,那么x可以割出x之上的1个连通分量,不是割点的孩子均是同1个连通分量,是割点的孩子各自分别是一个连通分量。
3)如果一个割点x,如果有些孩子是叶子节点,且该叶子节点的度为1,即连着x,那么该叶子节点会被割出来,单点也是连通分量。
1 //#include <bits/stdc++.h> 2 #include <iostream> 3 #include <cmath> 4 #include <cstdio> 5 #include <vector> 6 #include <cstring> 7 #include <climits> 8 using namespace std; 9 const int N=1005; 10 int mapp[N][N]; 11 int cntr, flag, up; //时间戳 12 int dfn[N], low[N], vis[N], iscut[N], cur[N]; //记录时间戳, 反边最顶点, 访问记录, 是否连通分量 13 14 bool DFS(int x, int far) 15 { 16 int chd=0; //孩子节点个数 17 int cnt=0, sum=0; 18 dfn[x]=low[x]=++cntr; //时间戳 19 vis[x]=1; //DFS必备 20 for(int i=1; i<=up; i++) 21 { 22 if(!mapp[i][x]) continue; 23 if(!vis[i]) //树边 24 { 25 chd++; 26 bool f=DFS(i,x); //i点只有一条边 27 low[x]=min(low[x],low[i]); 28 29 //判断连通分量的个数 30 if(!far && chd>1 || far && low[i]>=dfn[x] ) //能让x成为割点的孩子i 31 { 32 33 34 sum++; 35 iscut[x]=1; 36 if(iscut[i]||f) cnt++; //这个孩子是割点或者满足单点单边条件 37 } 38 } 39 else if(i!=far) 40 low[x]=min(low[x], dfn[i]); 41 } 42 if(!far && iscut[x]) 43 { 44 iscut[x]=cnt; //根节点 45 if(sum>cnt) iscut[x]++; 46 } 47 if( far && iscut[x]) //非根节点 48 { 49 if(!cnt ) iscut[x]=2; //没有“是割点”的孩子,那么只要自己是割点,起码可以割为两个连通分量 50 else if(sum==cnt) iscut[x]=cnt+1; //有“是割点”的孩子,那么每个“割点”孩子是1个连通分量,注:有可能还有非割点孩,他们算1个连通分量 51 else iscut[x]=cnt+2; 52 } 53 if(iscut[x]) flag=1; //无割点的标记' 54 //if(x==1) cout<<"**"<<sum<<endl; 55 56 if(!chd && low[x]==dfn[x] ) return true; //返回值用于判断是否是叶子,且没有反向边到fa之上 57 return false; 58 } 59 60 61 void cal() //n为边数 62 { 63 64 flag=cntr=0; 65 DFS(up,0); //深搜求割点数 66 int i; 67 for(i=1; i<=up; i++) //先确保是个连通图 68 if(cur[i]&&!vis[i]) 69 { 70 printf(" No SPF nodes\n"); 71 return ; 72 } 73 74 for(i=1; i<=up; i++) 75 if(cur[i]&&iscut[i]) 76 { 77 printf(" SPF node %d leaves %d subnets\n", i, iscut[i]); 78 } 79 80 if(!flag) printf(" No SPF nodes\n"); 81 } 82 void init() 83 { 84 up=0; 85 memset(cur,0,sizeof(cur)); 86 memset(mapp,0,sizeof(mapp)); 87 memset(vis,0,sizeof(vis)); 88 memset(dfn,0,sizeof(dfn)); 89 memset(low,0,sizeof(low)); 90 memset(iscut,0,sizeof(iscut)); 91 92 } 93 94 int main() 95 { 96 freopen("input.txt", "r", stdin); 97 int j=0, a, b; 98 while(1) 99 { 100 int cnt=0; 101 init(); 102 while(~scanf("%d",&a)) 103 { 104 if(!a && !cnt) return 0; //没边,a又为0,则结束了 105 if(!a) 106 { 107 printf("Network #%d\n",++j); 108 cal(); 109 printf("\n");break; 110 } 111 scanf("%d",&b); 112 up=max(max(up,a),b); //记录顶点号上限 113 cur[a]=cur[b]=1; //记录出现的顶点号 114 mapp[a][b]=mapp[b][a]=1; //矩阵 115 cnt++; //边数 116 } 117 } 118 return 0; 119 }