题目链接:http://poj.org/problem?id=1144
题目意思:求一个图的割点个数
割点:在一个无向图中,如果删除一个顶点以及所有相关联的边以后,图的连通分量增多,就称这个点为割点。
首先选定一个根节点,从该根节点开始遍历整个图(使用DFS),树上的边一定都是图上的边,称为树边,而图上其余没遍历的边则为非树边(回边)。
如果一个点不能通过非树边而回到比他树上的父亲的dfs序更小的点,那么如果把它树上的父结点删掉,它就不能通过其他方法与图的其他部分联通,即其父节点为割点。其实对于根节点,如果它有不止一个的子树,那它就是割点了。
void tarjan(int u) { low[u]=dfn[u]=++tot;//初始low[],dfn[] for(int i=head[u];i!=-1;i=edge[i].next)//遍历每条相连的边 { int v=edge[i].to;//与u相连的结点 if(!dfn[v])//如果v没被遍历过 { tarjan(v);//继续搜索v,找到low[v] low[u]=min(low[u],low[v]);//将u,v归为一个子树,所以取两者最小 if(low[v]>=dfn[u])//判断是否是割点 visit[u]++; } else//已经遍历过 low[u]=min(low[u],dfn[v]); } }
dfn[u]表示顶点u第几个被(首次)访问,low[u]表示顶点u及其子树中的点,通过非父子边(回边),能够回溯到的最早的点(dfn最小)的dfn值(但不能通过连接u与其父节点的边)。对于边(u, v),如果low[v]>=dfn[u],此时u就是割点。
但这里也出现一个问题:怎么计算low[u]。
假设当前顶点为u,则默认low[u]=dfn[u],即最早只能回溯到自身。
有一条边(u, v),如果v未访问过,继续DFS,DFS完之后,low[u]=min(low[u], low[v]);
如果v访问过(且u不是v的父亲),就不需要继续DFS了,一定有dfn[v]<dfn[u],low[u]=min(low[u], dfn[v])
转载自:https://www.cnblogs.com/collectionne/p/6847240.html
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x3f3f3f3f using namespace std; typedef long long ll; const int maxn=150; struct node{ int next,to; }edge[maxn*maxn]; int head[maxn],visit[maxn],low[maxn],dfn[maxn]; int n,m,ans,cnt,tot; void init()//初始化 { memset(head,-1,sizeof(head)); memset(visit,0,sizeof(visit)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); ans=cnt=tot=0; } void add(int u,int v)//连接两点 { edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt++; } void tarjan(int u) { low[u]=dfn[u]=++tot;//初始low[],dfn[] for(int i=head[u];i!=-1;i=edge[i].next)//遍历每条相连的边 { int v=edge[i].to;//与u相连的结点 if(!dfn[v])//如果v没被遍历过 { tarjan(v);//继续搜索v,找到low[v] low[u]=min(low[u],low[v]);//将u,v归为一个子树,所以取两者最小 if(low[v]>=dfn[u])//判断是否是割点 visit[u]++; } else//已经遍历过 low[u]=min(low[u],dfn[v]); } } int main() { while(~scanf("%d",&n)&&n) { init(); while(1) { int v; scanf("%d",&m); if(!m) break; while(1) { scanf("%d",&v); add(m,v); add(v,m); if(getchar()=='\n') break; } } tarjan(1); visit[1]--;//注意根结点1开始一定访问了一次 for(int i=1;i<=n;i++) if(visit[i]>0) ans++; cout<<ans<<endl; } return 0; }