poj3352 Road Construction 边双连通分量tarjan算法

http://poj.org/problem?id=3352

题意:有n个城市m条道路,一开始任何两个城市相互可达。现在需要某条修路,修路时该道路不可通行。然后需要搭建临时的桥,使得任何两个城市仍是相互可达的。求最少需要搭建的桥的数量。

题解:这是一个无向图,去掉一条边就不连通。那么这条边就是桥。现在要搭建临时的桥,搭建完后与原图一起,这个有向图就是边双连通的(边连通度大于1)。现在就是求加上几条边使得这个无向图是边双连通的。

首先tarjan求出边双连通分量,对边双连通分量进行缩点,那么就形成一棵树。

然后就是对于一棵树,如何加边使得它边双连通,有一个结论:加边数=(叶子节点数+1)/2。即加边后无度数为1的点。

代码:

#include<set>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<bitset>

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<ctime>

#include<iomanip>
#include<iostream>

#define debug cout<<"aaa"<<endl
#define d(a) cout<<a<<endl
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define MIN_INT (-2147483647-1)
#define MAX_INT 2147483647
#define MAX_LL 9223372036854775807i64
#define MIN_LL (-9223372036854775807i64-1)
using namespace std;

const int N = 1000 + 5;
const int mod = 1000000000 + 7;
const double eps = 1e-8;
int head[N],len;
int dfn[N],low[N],dfs_num;//dfn表示遍历深度,low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号
int deg[N],ans;

struct EdgeNode{
	int to,next;
}edge[N];

void add(int i,int j){
	edge[len].to=j;
	edge[len].next=head[i];
	head[i]=len++;
} 

void init(){
	mem(deg,0),mem(dfn,0),mem(low,0),mem(head,-1),ans=len=dfs_num=0;
}

void tarjan(int x,int from){
	dfn[x]=low[x]=++dfs_num;
	for(int i=head[x];i!=-1;i=edge[i].next){
		int temp=edge[i].to;
		if(temp==from) continue;
		if(!dfn[temp]){
			tarjan(temp,x);
			low[x]=min(low[x],low[temp]);
		}
		else{
			low[x]=min(dfn[temp],low[x]);
		}
	} 
}

void solve(int n){
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i,-1); 
		}
	}
	for(int i=1;i<=n;i++){
		for(int k=head[i];k!=-1;k=edge[k].next){
			int temp=edge[k].to;
			//不属于同一个边连通分量 
			if(low[i]!=low[temp]){
				deg[low[i]]++;
			}
		}
	} 
	//这里的i表示low[] 
	for(int i=1;i<=n;i++){
		if(deg[i]==1){
			ans++;
		}
	}
	ans=(ans+1)/2;
}

int main(){
	int n,m,u,v;
	char a;
	while(~scanf("%d%d",&n,&m)){
		init();
		for(int i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			add(u,v);
			add(v,u);
		}
		solve(n);
		printf("%d\n",ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值