题意: 给出n个点,每组第一个数据是 u,之后连续若干个点 v 建立u->v边, v->u边,询问有几个割点。
思路:
tarjan算法找割点,看了一天,才改懂模板!详情看注释
#include <stdio.h>
#include <algorithm>
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
const int maxn=100005;
vector <int>vec[maxn];///邻接表 stop,cnt,scnt置0
int id[maxn],dfn[maxn],low[maxn],s[maxn],cut[maxn], add_blocks[maxn];
int cnt,bridge;
void tarjan(int u,int pre)
{
low[u]=dfn[u]=++cnt;
int son=0;
for(int v=0;v<vec[u].size();++v)
{
int k=vec[u][v];
if(k==pre) continue ; ///防止重边
if( !dfn[k] )
{
son++;
tarjan(k,u);
low[u]=min(low[u],low[k]);
///桥, 一条无向边u,v 是桥, 当且仅当( u , v)为树枝边, 且dfn(u) < low(v)
if(low[k]>dfn[u])
{
bridge++;
/// is bridge;
}
///割点
///顶点u 为割点, (1)是根 有两个子树 (2)非根,存在满足(u,v)的树枝边 dfn[u]<=low[v] ( 即u 的 子树节点 v 必须通过u->v 才可以访问到u的祖先节点 )
if(u!=pre &&low[k]>=dfn[u] )///非树根,满足(2)
{
cut[u]=1;
add_blocks[u]++;
}
}
else if(low[u]>dfn[k]) low[u]=dfn[k];
}
if(u==pre&&son>1)
cut[u]=1;
if(u==pre) add_blocks[u]=son-1;
}
void init()
{
for(int i=0;i<maxn;i++)
vec[i].clear();
memset(dfn,0,sizeof(dfn));
memset(cut,0,sizeof(cut));
memset(low,0,sizeof(low));
memset(add_blocks,0,sizeof(add_blocks));
cnt=bridge=0;
}
void dfs(int n)
{
for(int i=0;i<n;i++)
{
if( !dfn[i] )
tarjan(i,i);
}
int ans=0;
for(int i=0;i<n;i++)
if(cut[i]) ans++;
printf("%d\n",ans);
}
int main()
{
int n,m;
while(~scanf("%d",&n),n )
{
init();
getchar();
int u,v;
char c;
while(scanf("%d",&u),u)
{
while(c=getchar())
{
if(c=='\n')
break;
int v;
scanf("%d",&v);
vec[u-1].push_back(v-1);
vec[v-1].push_back(u-1);
}
}
dfs(n);
}
}