何为割点?也就是题目中的关键点。在一个无向图中,去掉一个点,这个无向图会变成多个子图,那么这个点就叫做割点
同理,割边也是如此,如果去掉一条边,能让无向图变成多个子图,那么这条边叫做割边,所谓的桥。
那么tarjan是如何求的割点的呢?
如果u为割点,当且仅当满足下面的1/2
1、如果u为树根,那么u必须有多于1棵子树
2、如果u不为树根,那么(u,v)为树枝边,当Low[v]>=DFN[u]时。
割点的求法倒是看明白了,条件1的意思是若为根,下面如果只有一颗子树,也就是整个图是强连通,那么去掉根节点,肯定不会变成多个子图,因此也不会成为割点。只有大于一颗子树,去掉根节点,才会有两个或者2个以上的子图,从而才能成为割点
条件2也比较好理解,u不为树根,那么u肯定有祖先,如果存在Low【v】>=DFN【u】时,表示u的子孙只能通过u才能访问u的祖先,这也就是说,不通过u,u的子孙无法访问u的祖先,那么如果去掉u这个节点,就会至少分为两个子图,一个是u祖先,一个是u子孙的。
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
int v,next;
} edge[200010];
int head[20010],low[20010],dfn[20010],jilu[20010];
int cnt,times,n,root;
void add(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void tarjan(int u,int fa)
{
int flag=0;
int son=0;
dfn[u]=low[u]=++times;
for(int e=head[u]; e!=-1; e=edge[e].next)
{
int v=edge[e].v;
if(v==fa&&!flag)
{
flag=1;
continue;
}
if(!dfn[v])
{
tarjan(v,u);
son++;
low[u]=min(low[u],low[v]);
if((u==1&&son>1)||(u!=1&&dfn[u]<=low[v]))
jilu[u]=1;
}
else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
int ans;
while(~scanf("%d",&n)&&n)
{
int u,v;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(jilu,0,sizeof(jilu));
times=0;
cnt=0;
while(~scanf("%d",&u)&&u)
{
while(getchar()!='\n')
{
scanf("%d",&v);
add(u,v);
}
}
root=0;
tarjan(1,-1);
ans=0;
for(int i=1; i<=n; i++)
{
if(jilu[i])
ans++;
}
printf("%d\n",ans);
}
return 0;
}
下面两个我感觉差不多,但是不知道为什么是错误的,算了开学再研究吧,还是算法不熟悉
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
int v,next;
} edge[20010];
int head[20010],low[20010],dfn[20010],jilu[20010];
int cnt,times,n,root;
void add(int u,int v)
{
edge[cnt].v=v;
edge[cnt].next=head[u];
head[u]=cnt++;
edge[cnt].v=u;
edge[cnt].next=head[v];
head[v]=cnt++;
}
void tarjan(int u,int fa)
{
int flag=0;
dfn[u]=low[u]=++times;
for(int e=head[u]; e!=-1; e=edge[e].next)
{
int v=edge[e].v;
if(v==fa&&!flag)
{
flag=1;
continue;
}
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u]&&u!=1)
jilu[u]++;
else if(u==1)
{
root++;
}
}
else
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
int ans;
while(~scanf("%d",&n)&&n)
{
int u,v;
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(jilu,0,sizeof(jilu));
times=0; cnt=0;
while(~scanf("%d",&u)&&u)
{
while(getchar()!='\n')
{
scanf("%d",&v);
add(u,v);
}
}
root=0;
tarjan(1,-1);
ans=0;
if(root>1)
{
ans++;
}
for(int i=2; i<=n; i++)
{
if(jilu[i])
ans++;
}
printf("%d\n",ans);
}
return 0;
}