poj1523 求割点割边

以下内容为个人理解,有错误地方还请指出。



求割点

点u是割点的充分条件:

1. 如果u是dfs树的根,则u至少有两个子女。

2. 如果u不是dfs树的根,则它至少有一个子女w,从w出发,不可能通过和w、w的子孙相连的回边到达u的祖先。

 

u是割点的充要条件是:u或则是具有两个以上子女的深度优先生成树的根,或则虽然不是根,但它有一个子女w,使得low[w]>=dfn[u]。

 

求割边

有两种判断方法:

1. 当点u的子女v满足low[v]>dfn[u] 时,(u,v),(v,u)是一条割边;

2. 当无向图G中u,v相连,但是low[u]!=low[v]时,(u,v),(v,u)是一条割边。(因为同一边双连通分量的点,low值相同,详细可参考缩点)



//tarjan算法模板
vector<int>head[maxn];//邻接表
tarjan_dfs(int rt,int father)//father为rt的父节点
{
	int son=0;//rt的子女数
	dfn[rt]=low[rt]=dep_tmp++;
	vis[rt]=true;
	for(int i=0,len=head[rt].size();i<len;++i)
	{
                int v=head[rt][i];
		if(!vis[v])
		{
			son++;
			tarjan_dfs(v,rt);
			low[rt]=min(low[rt],low[v]);
		}
		else if(v!=father)//不要原路返回
		{
			low[rt]=min(low[rt],dfn[v]);
		}
		if(  (rt!=root && low[v]>=dfn[rt] )|| (rt==root && son>1) )
		{
			cut[rt]=true;//标记割点
		}
                if( low[v]>dfn[rt]
	        {
                        cut_edge[rt][v]=true;//标记割边
                        cut_edge[v][rt]=true;
                }
	}
}






Poj1523 SPF
给定一个连通网络(无向图),网络的结点数<=1000,求出这个网络的所有割点编号,并求出若删去其中一个割点k后,对应的,原网络会被分割为多少个连通分量
class  POJ_1523
{
protected:
	bool _map[maxn][maxn];
	int low[maxn];//特点
	int dfn[maxn];//点的dfs序号
	bool cut[maxn];//标记是否为割点
	bool vis[maxn];
	int dep_tmp;
	int root;//dfs起点 这里我设置为最小的点
	int size;//最大点的标号+1
	int I;
public:
	int input();
	bool solve();
	void initial();
	void dfs_net(int rt);
	void tarjan_dfs(int rt,int father);
	POJ_1523() {
		I=1;
		while(solve());
	}
};

void POJ_1523::initial()
{
	mem(_map); mem(low); mem(dfn); mem(vis); mem(cut);
	dep_tmp=1;
}
int POJ_1523::input()
{
	int x,y,num=0;
	root=maxn; size=0;
	while(scanf("%d",&x)&&x)
	{
		num++;
		scanf("%d",&y);
		root=min(root,min(x,y));
		size=max(size,max(x+1,y+1));
		_map[x][y]=_map[y][x]= true;
	}
	return num;
}
void POJ_1523::tarjan_dfs(int rt,int father)
{
	int son=0;
	dfn[rt]=low[rt]=dep_tmp++;
	vis[rt]=true;
	for(int i=1;i<size;++i)
	{
		if(_map[rt][i])
		{
			if(!vis[i])
			{
				son++;
				tarjan_dfs(i,rt);
				low[rt]=min(low[rt],low[i]);
			}
			else if(i!=father)//如果不是父节点,那就是一条回边
				low[rt]=min(low[rt],dfn[i]);
			if( (rt==root && son>1) ||  (rt!=root && low[i]>=dfn[rt]) )
				cut[rt]=true;
		}
	}
}
bool POJ_1523::solve()
{
	initial();
	if(!input())
		return false;
	int i;
	for(i=1;i<size;++i)
	{
		if(!vis[i])//只进一次 因为图是联通的
			tarjan_dfs(i,i);
	}
	if(I!=1)
		printf("\n");
	printf("Network #%d\n",I++);
	bool flag=false;
	for(i=root;i<size;++i)
	{
		if(cut[i])
		{
			mem(vis);
			vis[i]=true;
			int j,subnet_num=0;
			for(j=root;j<size;++j)
			{
				if(_map[j][i]&&!vis[j])
				{
					subnet_num++;
					dfs_net(j);
				}
			}
			printf("  SPF node %d leaves %d subnets\n",i,subnet_num);
			flag=true;
		}
	}
	if(!flag)
		printf("  No SPF nodes\n");
	return true;
}
void POJ_1523::dfs_net(int rt)
{
	vis[rt]=true;
	int i;
	for(i=root;i<size;++i)
	{
		if(!vis[i]&&_map[i][rt])
			dfs_net(i);
	}
}
int main() {  POJ_1523 final_ans;  }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值