题意:给出一个网络(不一定连通),求所有的割点,以及割点可以切分出多少个连通分量。
思路:很多种情况。
(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 }
















