题解:
这道题的输入有点恶心,我是看的别人弄的,这是一道求割点的模板题,套模板就行了。但是我有一件事情不明白,为什么是关心v和pre呢?关心v是否连接到u的父亲pre呢?那要是连接到u的爷爷或者祖先呢?怎么办?求大佬解答。不懂的话可以去看看这位大佬的博客:
http://blog.csdn.net/wtyvhreal/article/details/43530613
题外话:
要是我啥时候能自己弄一篇解释算法就厉害了ORZ。。。
#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;
const int MAXN=110;
vector<int>map[MAXN];
int low[MAXN];
int dfn[MAXN];
int stack[MAXN],head;
int belong[MAXN];
bool instack[MAXN];
bool cut[MAXN];//割边
int bridge;//割边(桥)
int Index,sig;//这狗血的编译器不能编译index,index好像在UVA中有意义。。。MD
int n,m;
void init()
{
memset(low,0,sizeof(low));
memset(belong,0,sizeof(belong));
memset(dfn,-1,sizeof(dfn));
memset(stack,0,sizeof(stack));
memset(instack,false,sizeof(instack));
memset(cut,false,sizeof(cut));
for(int i=0;i<=n;i++) map[i].clear();
Index=1;
sig=0;
head=0;
}
void Tarjan(int u,int pre)
{
low[u]=dfn[u]=Index;
Index++;
stack[++head]=u;
instack[u]=true;
int son=0;
for(int i=0;i<map[u].size();i++)
{
int v=map[u][i];
if(dfn[v]==-1)
{
son++;
Tarjan(v,u);
low[u]=min(low[u],low[v]);
//桥
//一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。
if(low[v]>dfn[u])
{
bridge++;
}
//割点
//一个顶点u是割点,当且仅当满足(1)或(2)
//(1) u为树根,且u有多于一个子树。
//(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,
//即u为v在搜索树中的父亲),使得DFS(u)<=Low(v)
if(u!=pre&&low[v]>=dfn[u])
{
cut[u]=true;
}
}
else if(v!=pre)//这里一定要有v!=pre,代表的是v不能回溯到u的父亲节点。(但是我有一事不明,为什么是只关心他的父亲呢,他的爷爷,他祖先不用管理吗?要是连到他的爷爷或者祖先节点呢?希望有dalao解答)
{
//还有就是我用instack[v]&&v!=pre也是可以的,我还是不明白上面那行说的情况啊,ORZ
low[u]=min(low[u],dfn[v]);
}
}
//树根,分支数大于1的也算是割点
if(u==pre&&son>1) cut[u]=true;
if(low[u]==dfn[u])
{
int temp;
sig++;
while(1)
{
temp=stack[head--];
belong[temp]=sig;
instack[temp]=false;
if(temp==u)
break;
}
}
}
void solve()
{
bridge=0;
int ans=0;
for(int i=1;i<=n;i++)
{
if(dfn[i]==-1)
Tarjan(i,i);
}
for(int i=1;i<=n;i++)
if(cut[i])
ans++;
printf("%d\n",ans);
}
int main()
{
while(~scanf("%d",&n)&&n)
{
init();
int u,v;
char c;
while(~scanf("%d",&u)&&u)
{
while(~scanf("%d%c",&v,&c))
{
map[u].push_back(v);
map[v].push_back(u);
if(c=='\n')
break;
}
}
solve();
}
}