Poj 1523 SPF(割点 + tarjan算法)

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值