假若在删去顶点v以及和v相关联的各边之后,将图的一个连通分量分割成两个或两个以上的连通分量,则称顶点v为该图的一个割点。
生成树为从根结点出发深度优先搜索遍历图,未遍历到的边为虚边。
割点判定:
(1)若生成树的根有两棵或两棵以上子树,则此根结点必为割点
(2)若生成树中某个非叶子结点v,其某棵子树的根和子树中的其他结点均没有指向v的祖先的回边,则v为割点
具体请看数据结构课本。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <map>
using namespace std;
const int maxN=110;
vector<int> e[maxN];
int n,ind,dfn[maxN],low[maxN],root;
bool flag[maxN];
void tarjan(int u){
int son=0;
dfn[u]=low[u]=++ind;
for(vector<int>::iterator it=e[u].begin();it!=e[u].end();++it){
int v=*it;
if(!dfn[v]){
tarjan(v);
son++;
low[u]=min(low[v],low[u]);
if((u==root&&son>=2)||(u!=root&&low[v]>=dfn[u])){
flag[u]=true;
}
}
else low[u]=min(low[u],dfn[v]);
}
}
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
while(cin>>n,n){
int u;
ind=0;
memset(flag,false,sizeof(flag));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
for(int i=1;i<=n;i++) e[i].clear();
while(cin>>u,u){
while(getchar()!='\n'){
int v;
cin>>v;
e[u].push_back(v);
e[v].push_back(u);
}
}
root=1;
int ans=0;
tarjan(root);
for(int i=1;i<=n;i++)
if(flag[i])
ans++;
cout<<ans<<endl;
}
return 0;
}