poj1523 - SPF

133 篇文章 0 订阅

                                  想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410
                                  转载请注明出处:http://blog.csdn.net/wangjian8006

 

题目大意:拿左边的图举例,有一个网络,在这个网络只能是点对点通信,如果3号节点出故障了,那么1号节点与2号节点,4号节点和5号节点还是可以通信的,不过1,2号不能和4,5号节点通信了。这样3号节点就是一个SPF节点,并且当3号节点出故障了分为了2个子网络
差不多就是说与许多点,有些点之间有无向边,问删除某个点之后,可以将图分为几块

 

 

解题思路:一个裸的求割点的题目,割点的定义是,在一个连通图,删除某一个点之后,这个图不再连通,那么称这个点为割点,又可以叫关节点,而分为几块的意思是求删除这个点之后有多少个连通分量
一个暴力算法是,将每个点删除之后求连通分量再恢复这个点,但是时间复杂度是O(n^3)

但是根据定理用tarjan算法可以以O(n^2)的时间复杂度求出:
点x是关节点的充分必要条件是
1.x是深搜的生成树的树根,那么如果x有2个以上的孩子结点,那么x是割点,并且连通分量就是孩子结点的数目
2.x不是树根,那么如果x有一个孩子结点,使得dfn[x]<=low[y],那么x也是一个割点,连通分量就是,看有多少个孩子
结点的符合这个条件,连通分量=符合条件的孩子数目+1

注:dfn代表深搜的先后顺序,low代表最低深度优先数,如果不懂,可以去了解下tarjan算法

tarjan:http://blog.csdn.net/wangjian8006/article/details/7892084

 

/*
Memory 1172K
Time    16MS 
*/
#include <iostream>
using namespace std;
#define MAXV 1010
#define min(a,b) (a>b?b:a)

bool map[MAXV][MAXV];		//保存图
int rcnt;					//记录输入的结点
int dfn[MAXV],low[MAXV];	//深度与最低深度优先数
bool vis[MAXV];				//标记结点是否被寻找
int son;					//树根的子女结点的个数
int subnets[MAXV];			//记录删除i点后的连通分量
int count;					//记录访问

void init(){
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(vis,0,sizeof(vis));
	memset(subnets,0,sizeof(subnets));
	son=0;
	count=1;
	dfn[1]=low[1]=1;	//对根节点初始化
	vis[1]=1;
}

void dfs(int x){			//tajrjan
	for(int i=1;i<=rcnt;i++){
		if(map[x][i]){
			if(!vis[i]){
				vis[i]=1;
				dfn[i]=low[i]=++count;
				dfs(i);
				low[x]=min(low[x],low[i]);
				if(low[i]>=dfn[x]){
					if(x!=1) subnets[x]++;
					if(x==1) son++;
				}
			}else low[x]=min(low[x],dfn[i]);
		}
	}
}

void work(){
	init();
	dfs(1);
}
void Output(){
	int flag=0;
	if (son>1) subnets[1]=son-1;			//到时候输出要+1
	for(int i=1;i<=rcnt;i++){
		if(subnets[i]){
			printf("  SPF node %d leaves %d subnets\n",i,subnets[i]+1);
			flag=1;
		}
	}
	if(!flag) printf("  No SPF nodes\n");
		printf("\n");
}

void cntv(int x){
	rcnt=(rcnt>x?rcnt:x);
}

int main(){
//	freopen("d:\\test.in","r",stdin);
	int a,b,Case=0;
	while(scanf("%d",&a) && a){
		memset(map,0,sizeof(map));
		scanf("%d",&b);
		map[a][b]=map[b][a]=1;
		rcnt=0;
		cntv(a),cntv(b);
		while(scanf("%d",&a) && a){
			scanf("%d",&b);
			map[a][b]=map[b][a]=1;
			cntv(a),cntv(b);
		}
		work();
		printf("Network #%d\n",++Case);
		Output();
	}
	return 0;
}


 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值