zoj1119--双连通--寻找关节点---计算子网数

/*
通过这个过程,可以发现一条规律:
	当v是树根,如果它有2个或者更多儿子,那么它是一个关节点。
	当v不是树根,当且仅当它有至少一个儿子w, 且从w出发,不能通过w的后代顶点组成的路径和一条回退边到底u 的任意一个祖先顶点,此时v 是一个关节点。 
其道理很明显,如果树根包含多个儿子,那么把根节点去掉,整棵树自然被分成多个不相干的部分,图也就断开了。如果v是非根顶点,如果其子树中的节点均没有 指向v祖先的回边,那么去掉v以后,将会把v及其子树与图的其他部分分割开来,v自然是关节点。



因此,我们在深搜过程中计算出 dfn 值和 low 值,如果发现 u有一个儿子w ,使得 low(w) >= dfn(u), 那么u就是关节点。(一个就够了,一个就说明他可以长生一个连通分量)


*/
#include<stdio.h>
#include<string.h>
int sub[1010];
int head[1010];
struct node
{
	int v,next;
}e[100010];
int yong;
int fang[1010],low[1010];
void add(int a,int b)
{
	e[yong].v=b;
	e[yong].next=head[a];
	head[a]=yong;
	yong++;
	e[yong].v=a;
	e[yong].next=head[b];
	head[b]=yong;
	yong++;
}
void dfs(int x)//为双连通不需要栈?不需要保存属于同一连通分量的节点,后面的没想明白:访问其他连通分量上的的节点(可能同一方向上的点都一次性访问过了,边是双向的,能访问到的除非是关节点,否则就是同一连通分量上的,)无意义
{
	int v,en;
	fang[x]=low[x]=yong++;
	for(en=head[x];en;en=e[en].next)
	{
		v=e[en].v;
		if(fang[v]==0)
		{
			dfs(v);
			if(low[v]<low[x])
				low[x]=low[v];
			if(low[v]>=fang[x])//为什么要与fang[x]比较,因为只要low[v]<low[x],low[x]就会被更新,low[x]永远等于low[v],需要比的不是父节点和孩子节点最低能探到哪儿,而是孩子节点最低能探到的位置是否其父还低
				sub[x]++;//若u为根,则他的所有儿子的low都将>=他的visited,故,subset[root]统计的是root的孩子数      只有未访问的才可以进来,若在上个孩子的链中出现了其他孩子,其他的孩子i后就不能算作孩子了
		}else if(fang[v]<low[x])//low[v]会放回其上级,从而更新到比fang[v]还小;那么其孩子w也会更新到low[v],直接就比fang[v]小,从而忽略了孩子成为双连通分量的可能。故只能使用fang[v],
			low[x]=fang[v];//双连通担心的是他提前更新,导致他的孩子也提前更新,从而fang[本节点]>low[孩子]=low[本节点]=low[本节点的父节点]
	}
}
int main()
{
	int a,b,no=1,f,i,max;
	scanf("%d",&a);
	while(a)
	{
		max=0;
		f=0;
		yong=1;
		if(a>max)
			max=a;
		memset(head,0,sizeof(head));
		do{
			if(a>max)
				max=a;
			scanf("%d",&b);
			if(b>max)
				max=b;
			add(a,b);
			scanf("%d",&a);
		}while(a);
		yong=1;
		memset(sub,0,sizeof(sub));
		memset(fang,0,sizeof(fang));
		dfs(1);
		if(sub[1])//统计的实际上是他的直接孩子数,也可以理解为子网数,若有一个,则他一定在那个里边,故删除它不能产生子网,若是>1则记录的就是它能产生的子网数,-1是因为其他节点的数量都比实际小一,这样做事为了配合输出(他的记录不少一是因为他没有父节点,他的删除不会使其父成为子网)
			sub[1]--;
		printf("Network #%d\n",no++);
		for(i=1;i<=max;i++)
		{
			if(sub[i])
			{
				f=1;
				printf("  SPF node %d leaves %d subnets\n",i,sub[i]+1);//节点伤处以后,除了统计到的回滚不能越过他的可以形成子网外,他的父节点也可以形成子网
			}
		}
		if(!f)
			printf("  No SPF nodes\n");
		scanf("%d",&a);
		if(a)
			printf("\n");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值