链接:http://poj.org/problem?id=1523
题意:有一个计算机网络,如果去掉其中某一台计算机后,整个网络不能相互连通,
问这样的计算机有几台。并且求出若去掉这样一台计算机后,整个网络变成几个连通分量。
解析:求图的割点,并求删除此割点后,图变成几个连通分量。
用tarjan算法求图的割点。
割点有两种情况:
1.割点为树根。如果树根有大于1个的孩子,则该树根为割点。删除该割点,连通分量=孩子个数;
2.割点为普通节点。如果一个普通节点(非根节点)满足dfn[u]<=low[v](edge(u,v)),
则该节点为割点。删除该节点,连通分量=满足该条件的子节点(v为u的子节点)的个数+1。
#include<cstdio>
#include<cstring>
#define MAXN 1005
using namespace std;
struct Edge
{
int to;
int next;
}edge[MAXN*4];
struct Cut
{
int is;
int ans;
}cut[MAXN];
int head[MAXN],dfn[MAXN],low[MAXN];
int ecnt,index,root=1;//设置dfs树的根节点为1
void add(int u,int v)//无向图,双向边
{
edge[ecnt].to=v;
edge[ecnt].next=head[u];
head[u]=ecnt++;
edge[ecnt].to=u;
edge[ecnt].next=head[v];
head[v]=ecnt++;
}
void tarjan(int u,int fa)
{
int i,v,cnt=0; //cnt记录节点的孩子个数
dfn[u]=low[u]=++index;
for(i=head[u];i!=-1;i=edge[i].next)
{
v=edge[i].to;
if(v==fa) //不能再回去访问父节点
continue;
if(!dfn[v])
{
tarjan(v,u);
cnt++;
if(low[u]>low[v])
low[u]=low[v];
if(root==u&&cnt>1)//根为割点的情况
{
cut[u].is=1;
cut[u].ans=cnt-1;//这里cnt减去1,是为了符合最后的输出,因为最后的输出加了1
}
if(root!=u&&dfn[u]<=low[v])
{
cut[u].is=1;
cut[u].ans++; //连通分量=满足该条件的子节点(v为u的子节点)的个数+1,这里加1放在了最后的输出
}
}
else if(low[u]>dfn[v])
low[u]=dfn[v];
}
}
int main()
{
int a,b,sig,num=1,max,i;
while(scanf("%d",&a),a)
{
scanf("%d",&b);
ecnt=0;
max=0;
memset(head,-1,sizeof(head));
if(a>max)
max=a;
if(b>max)
max=b;
add(a,b);
while(scanf("%d",&a),a)
{
scanf("%d",&b);
if(a>max)
max=a;
if(b>max)
max=b;
add(a,b);
}
memset(cut,0,sizeof(cut));
memset(dfn,0,sizeof(dfn));
index=0;
sig=0;
tarjan(1,-1);//初始化根节点的父节点为-1,也就是没有父节点
printf("Network #%d\n",num++);
for(i=1;i<=max;i++)
if(cut[i].is)
{
printf(" SPF node %d leaves %d subnets\n",i,cut[i].ans+1);
sig=1;
}
if(!sig)
printf(" No SPF nodes\n");
printf("\n");
}
return 0;
}