题目链接:poj 1523
题意描述::略
分析:求割点的算法很明显的是用tarjan算法,当一条边u---v时有low[v]>=dfn[u](表示v不能更新到u更以前的节点)这就直接说明了u点u之前的和u的子树给分开,那么u就为割点,但这里有个问题对于根节点用这个判断是否为割点就会出错,看看这个例子:(1,2) (2,3) (3,1),设根节点为1,那么从1开始更新对于节点1的dfn[1]=1,low[1]=1,假设这时先搜到2(注意求割点的时候无向图可以反向更新)则有dfn[2]=2,low[2]=2;因为low[2]>dfn[1]所以low[2]=1,现在搜索到节点3,那么dfn[3]=3,low[3]=2,更新之后返回,因为dfn[2]>low[3],所以2不是割点,返回到节点2,因为low[2]>=dfn[1]=2,所以节点1为割点,显然该图中是没有割点的,所以这样的求法对根节点是不对的,那么只要特殊处理根节点就好,如果根节点的子树的个数超过1则能够说明根节点为割点(注意这里的子树是 这样形成的:从根节点进行搜索,每次沿着以个分支搜索,若为节点没有被搜索完,那么可以继续搜索,分支数将会增加);这里的每个割点属于的块数是如何求的呢? 这里我们直接设一个标记数组来记录割点的块数,当存在满足割点条件的点时,我们就将对应的数组增1,那么这样数组里面存放的数字就是(块数-1)(因为从根节点方向来的那一块不会将割点判断为割点,所以记录块的个数,比总块的个数少1),这个结论对根节点同样适用,求无向图的割点时是否有重边对求割点是没有影响的
贴代码先:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1005;
bool g[N][N];
int n, m;
int dfn[N],low[N],index,son,num[N];
int Min(int a, int b)
{
return a>b?b:a;
}
void Cutvedex(int u)
{
dfn[u]=low[u]=index++;
for(int v=n;v<=m;v++)
{
if(u==v)continue;
if(g[u][v])
{
if(dfn[v]==-1)
{
if(u==n)
son++;
Cutvedex(v);
low[u]=Min(low[u],low[v]);
if((u!=n&&low[v]>=dfn[u])||(u==n&&son>1))
num[u]++;
}
else low[u]=Min(low[u],dfn[v]);
}
}
}
int main ()
{
int k=1;
int x,y,i;
bool tag;
while(scanf("%d",&x)!=EOF)
{
if(x==0)
break;
tag=false;
memset(g,0,sizeof(g));
m=-1,n=1005;
while(1)
{
if(tag)
scanf("%d",&x);
tag=true;
if(x==0)break;
scanf("%d",&y);
g[x][y]=1;
g[y][x]=1;
if(m<x)m=x;
if(m<y)m=y;
if(n>x)n=x;
if(n>y)n=y;
}
printf("Network #%d\n",k++);
memset(num,0,sizeof(num));
memset(dfn,-1,sizeof(dfn));
memset(low,0,sizeof(low));
index=1;son=0;
Cutvedex(n);
tag=false;
for(i=n;i<=m;i++)
if(num[i])
{
tag=true;
printf(" SPF node %d leaves %d subnets\n",i,num[i]+1);
}
if(!tag)printf(" No SPF nodes\n");
printf("\n");
}
return 0;
}