tarjan桥与求双连通

poj3117,题目大意增加几条边,将图变为双连通。

分析:无向图跑tarjan,找出双连通,也就是环,然后缩点。缩点后重新处理图,统计叶子节点数leaf,总数即为(leaf+1)/2。

注意坑点:本题的输入数据有重边,但是重边只能作为一条边,要去重。

/*
图的割边
即在一个无向连通图中,如果删除某条边后,图不再连通,则成为割边。

求割边时,只需要将求割点的算法修改一个符号即可。只需要将low[v]>=num[u]改为low[v]>num[u]。
因为low[v]>=num[u]代表的是v不可能在不经过父亲u而回到祖先。如果low[v]=num[u],表示还可以回到父亲。而low[v]>num[u]则表示连父亲都回不到了。倘若顶点v不能回到祖先,也没有另外一条路能回到父亲,那么u-v就是割边。
*/
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;
const int maxn=5009;
struct node {
	int to,nxt;
} e[4*maxn];
int n,m,cnt=0,index=0,head[maxn],dfn[maxn],low[maxn],du[maxn],map[maxn][maxn];
void add(int u,int v) {
	e[cnt].to=v;
	e[cnt].nxt=head[u];
	head[u]=cnt++;
}
void tarjan(int u,int fa) {
	dfn[u]=low[u]=++index;
	for(int i=head[u]; i>-1; i=e[i].nxt) {
		int v=e[i].to;
		if(!dfn[v]) {
			tarjan(v,u);
			low[u]=min(low[u],low[v]);
			//if(low[v]>dfn[u])iscut[i]=iscut[i^1]=1; //割边
		} else if(v!=fa)
			low[u]=min(low[u],dfn[v]);
	}
}

int main() {
	cin>>n>>m;
	int u,v;
	memset(head,-1,sizeof(head));
	for(int i=1; i<=m; i++) {
		scanf("%d%d",&u,&v);
		if(!map[u][v]) {
			add(u,v);
			add(v,u);
			map[u][v]=1;
		}

	}
	for(int i=1; i<=n; i++)
		if(!dfn[i])tarjan(i,-1);
	//for(int i=1;i<=n;i++)
	//	cout<<i<<" dfn="<<dfn[i]<<" low"<<low[i]<<endl;
	//tarjan(1,-1);
	for(int i=1; i<=n; i++) //缩点统计
		for(int j=head[i]; j>-1; j=e[j].nxt) {
			int v=e[j].to;
			if(low[v]!=low[i])du[low[i]]++;
		}
	int leaf=0;
	for(int i=1; i<=n; i++)
		if(du[i]==1)leaf++;
	printf("%d\n",(leaf+1)/2);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值