题目来源:http://poj.org/problem?id=1144
除了输入处理起来比较麻烦,别的都还好。这是一道割点的模板题,题意是给出一个无向图,让你求出图中割点的个数。
使用dfs树的方法,用三个数组来实现:
vis[cur]表示节点cur当前的状态,0表示未访问,1表示在栈中,2表示已经访问过。
dfn[cur]记录点cur被访问时的深度,也就是其在这棵dfs树中的深度。
low[cur]记录点cur及其子树中可以连回的深度最小的祖先的深度。
在dfs的过程中对当前节点cur,与其相连的节点x有两种情况:
如果x没被访问过中,则递归访问节点x,并用x的low值更新cur 的low值。
如果x在栈中,说明图中存在环,即cur可连回x,则用x的深度更新cur的low值。
这样判断一个点是否为割点,分两种情况:
如果节点cur是dfs树中的根节点,那么如果cur的孩子数量大于1,则cur是割点(如果删去cur,图将不再联通,故此时cur显然是割点)。
如果cur不是根节点,那么如果low[cur]>=dfn[cur],也就时cur及其子树中所有节点能连回的深度均不比cur的要小,这样cur是割点。
代码:
#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
using namespace std;
const int maxn=1100;
int n;
int cnt=0;
bool cut[maxn];
int head[maxn];
int vis[maxn];
int dfn[maxn];
int low[maxn];
struct data{int to,next;}e[maxn*2];
void ins(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void cut_bridge(int cur,int fa,int dep)
{
vis[cur]=1;dfn[cur]=low[cur]=dep;
int child=0;
for(int i=head[cur];i;i=e[i].next)
{
int x=e[i].to;
if(x!=fa&&vis[x]==1)
{
if(dfn[x]<low[cur])low[cur]=dfn[x];
}
if(vis[x]==0)
{
cut_bridge(x,cur,dep+1);
child++;
if(low[x]<low[cur])low[cur]=low[x];
if((fa==-1&&child>1)||(fa!=-1&&low[x]>=dfn[cur]))cut[cur]=1;
}
}
vis[cur]=2;
}
int main()
{
scanf("%d",&n);
while(n)
{
cnt=0;
memset(e,0,sizeof(e));
memset(cut,0,sizeof(cut));
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
int u;
scanf("%d",&u);
while(u)
{
int v;
while(getchar()!='\n')
{
scanf("%d",&v);
ins(v,u);
ins(u,v);
}
scanf("%d",&u);
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
cut_bridge(i,-1,0);
}
}
int ans=0;
for(int i=1;i<=n;i++)if(cut[i])ans++;
printf("%d\n",ans);
scanf("%d",&n);
}
return 0;
}