割点 :去掉该点后原来的图不连通(出现好几个连通分量),该点被称为割点。
注意删除某点意味着和该点关联的边也全部删除
求割点的伪代码
DFS(v1,father): dfn[v1] = low[v1] = ++dfsClock vis[v1] = true child = 0 for each egde(v1,v2) in E: if(vis[v2] == false) : //(v1,v2)是父子边 DFS(v2,v1) child++ low[v1] = Min(low[v1],low[v2]) if(v1 != root && low[v2] >= dfn[v1]) v1 is cut point if(low[v2] > dfn[v1]) edge(v1,v2) is bridge end if elseif(v2 != father && vis[v2] == true) : //v2已经被访问过,(v1,v2)是反向边 low[v1] = Min(low[v1],dfn[v2])//此时要更新low[v1] end if Init() : dfn[] = low[] = vis[] = 0 dfsClock = 0
dfn【i】 记录 DFS树的深度,也就是,DFS中第几次遍历到的点,dfn的值就为几;
low【i】存储的是 i节点及i节点的后续节点通过反向边所能达到的最小的DFS深度
【更新low】
如果所处的边是树边,low【v1】=min(low【v1】,low【v2】)
如果所处的边是反向边,low【v1】=min(low【v1】,dfn【v2】)
【判断割点】
假设v2是v1的子节点
如果v1是根节点, 那么如果v1有两个或者两个以上的子节点,那么v1是割点
如果v1不是根节点,那么如果low【v2】 >=dfn【v1】,可以判断 v1是割点。
给个模板题 poj1144 network
题目链接:http://poj.org/problem?id=1144
【题目大意】
Telephone Line Company 在城市与城市之间建立了电话网络,有这样一种城市S, 如果S的电话服务崩溃了,那么 其他城市的电话并不能联通(就是至少有一个城市其他城市都连接不到)。
给你城市 与城市之间的关系, 找出有多少个 这样的城市 S;
典型的模板题, S就是图中的 割点
【源代码】
#include <iostream> #include <vector> #include <cstdio> using namespace std; const int maxn =110; vector<int>G[maxn]; //用邻接链表存图 int dfn[maxn],low[maxn],dfsClock; bool cutNode[maxn]; //记录是否为割点 int vis[maxn]; void DFS(int v1,int father){ int child =0; vis[v1]=true; dfn[v1]=low[v1]= ++dfsClock; for(int i=0;i<G[v1].size();i++){ int v2=G[v1][i]; if(!vis[v2]){ DFS(v2,v1); child++; low[v1] = min(low[v1],low[v2]); if(v1 != 1 && low[v2] >= dfn[v1]) //割点 cutNode[v1]=true; if(v1 == 1 &&child >=2){ //如果是起点 cutNode[v1]=true; } } else if(vis[v2]&&v2!=father){ //如果是 反向边 low[v1]=min(low[v1],dfn[v2]); } } } void init(){ for(int i=0;i<maxn;i++){ G[i].clear(); dfn[i]=low[i]=0; cutNode[i]=0; vis[i]=0; } dfsClock = 0; } int main(){ int n; while(scanf("%d",&n)!=EOF&&n){ init(); int tmp;char c; int v1,v2; while(scanf("%d",&v1)&&v1){ while((c=getchar())!='\n'){ scanf("%d",&v2); G[v1].push_back(v2); G[v2].push_back(v1); } } DFS(1,-1); int ans=0; for(int i=1;i<=n;i++){ if(cutNode[i]) ans++; } printf("%d\n",ans); } return 0; }