http://poj.org/problem?id=1523
题意:给一个连通网络,其节点数目<= 1000,求出其割点编号,并求出删除该割点后会被分割成几个连通块。
思路:
tarjan算法:从第一个节点开始深度优先搜索,同时记录每个节点i的访问次序以及该节点所能到达的最高辈分(深度最低)的祖先,对这两个数比较确定这个点是否是割点;同时用subnets数组记录,若是割点并且是搜索树的根节点,则它的儿子数就是删除节点 i 后得到的连通分量,若是割点并且是非根节点,当第一次遍历到该节点时还不能确定它是不是割点,之后对于该节点的每一棵子树若能判断出该节点是割点,则subnets[i]加1.
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
const int maxn = 1010;
vector <int> edge[maxn];//邻接表存储
int subnets[maxn];//记录节点是否为割点,当是割点时记录删除它后的连通分量数
int dfn[maxn];//深度优先搜索时的次序(时间戳)
int low[maxn];//能追溯到的深度最浅的祖先
int vis[maxn];//是否访问过
int cnt;
int son;//根节点儿子数
int maxv;//最大节点数
void Init()
{
memset(vis,0,sizeof(vis));
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(subnets,0,sizeof(subnets));
cnt = 0;
son = 0;
}
void tarjan(int u)
{
vis[u] = 1;
dfn[u] = low[u] = ++cnt;
for(int i = 0; i < (int)edge[u].size(); i++)
{
int v = edge[u][i];
if(!vis[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u])//u是割点
{
if(u >= 2)
subnets[u]++;
else son++;
}
}
else
low[u] = min(low[u],dfn[v]);
}
}
void output(int item)
{
int flag = 0;
printf("Network #%d\n",item);
if(son > 1) subnets[1] = son - 1;
for(int i = 1; i <= maxv; i++)
{
if(subnets[i])
{
flag = 1;
subnets[i] += 1;
printf(" SPF node %d leaves %d subnets\n",i,subnets[i]);
}
}
if(!flag)
printf(" No SPF nodes\n");
printf("\n");
}
int main()
{
int u,v,f,flag;
for(int item = 1; ; item++)
{
for(int i = 1; i < maxn; i++)
edge[i].clear();
f = 0;
maxv = -1;
while(1)
{
scanf("%d",&u);
maxv = max(maxv,u);
if(u == 0 && f == 0)
{
flag = 1;//输入结束
break;
}
if(u == 0 && f != 0)
{
flag = 2;//改组输入结束
break;
}
scanf("%d",&v);
maxv = max(maxv,v);
f = 1;
edge[u].push_back(v);
edge[v].push_back(u);
}
if(flag == 2)
{
Init();
tarjan(1);
output(item);
}
if(flag == 1)
break;
}
return 0;
}