题目链接:http://poj.org/problem?id=1144
题目意思:求一个图的割点个数
割点:在一个无向图中,如果删除一个顶点以及所有相关联的边以后,图的连通分量增多,就称这个点为割点。
1、如果u为树根,那么u有2棵以上子树,则是割点。这种情况容易处理
2、如果u不为树根,那么(u,v)为树枝边,当Low[v]>=DFN[u]时。解释如下:
选定一个根节点,从该根节点开始遍历整个图(使用DFS),树上的边一定都是图上的边,称为树边,而图上其余没遍历的边则为非树边(回边)。
如果一个点不能通过非树边而回到比他树上的父亲的dfs序更小的点,那么如果把它树上的父结点删掉,它就不能通过其他方法与图的其他部分联通,即其父节点为割点。
void tarjan(int u,int fa)//搜索点u,fa为u的根节点。 { low[u]=dfn[u]=++tot; int son=0;//记录子树个数 for(int i=h[u];i!=-1;i=e[i].nxt) { int v=e[i].v; if(!dfn[v]) { tarjan(v,fa); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=fa)//情况二,v通过回边返回不了比u更早的点。 flag[u]=1; if(u==fa)//u是根节点,v这是u的一个子树 son++; } else low[u]=min(low[u],dfn[v]); } if(son>=2&&u==fa)//情况一,根节点子树>=2 flag[u]=1; }
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,即通过回边搜索,回溯时 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],flag[maxn],low[maxn],dfn[maxn]; int n,m,ans,cnt,tot; void init()//初始化 { memset(head,-1,sizeof(head)); memset(flag,0,sizeof(flag)); 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,int fa)//搜索点u,fa为u的根节点。 { low[u]=dfn[u]=++tot; int son=0;//记录子树个数 for(int i=head[u];i!=-1;i=edge[i].next) { int v=edge[i].to; if(!dfn[v]) { tarjan(v,fa); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]&&u!=fa)//情况二,v通过回边返回不了比u更早的点。 flag[u]=1; if(u==fa)//u是根节点,v这是u的一个子树 son++; } else low[u]=min(low[u],dfn[v]); } if(son>=2&&u==fa)//情况一,根节点子树>=2 flag[u]=1; } 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()==' ') break; } } tarjan(1,1); for(int i=1;i<=n;i++) if(flag[i]>0) ans++; cout<<ans<<endl; } return 0; }