poj 3177 Redundant Paths

32 篇文章 0 订阅
类型:双连通分量 + 缩点

题目:http://poj.org/problem?id=3177

来源: USACO 2006 January Gold
思路:双连通分量不需要添加新边,将各个双连通分量看做一个点的话,生成树的边即为原图中的桥,此时

添加的边数 = (树中度为1的节点 + 1) / 2

进行Tarjan算法过程中,统计不同桥的信息,如果某边不是桥,则两节点属同一连通分量,进行缩点(并查集实现)

然后记录每个节点在新树中的编号,最后通过桥边记录新树各边的度数,统计度数为1的个数

// poj 3177 - Redundant Paths
// ac 256K 0MS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdio>
using namespace std;

#define MIN(a,b) (a < b ? a : b)
#define clr(a,b) memset(a,b,sizeof(a))
#define MAXN 5005
#define MAXM 20010

bool vis[MAXN];
int F, R, bri_num, cnt, num, node;
int d[MAXN];
int tree_node[MAXN]; //存储每个连通分量对应新树的节点
int root[MAXN];
int step[MAXN];
int low[MAXN];
int head[MAXN];
int bridge[MAXN][2]; //存储图中的桥
struct edge {
	int v,nxt,id;
}e[MAXM];

void addedge(int u,int v,int id) {
	e[cnt].id = id;
	e[cnt].v = v;
	e[cnt].nxt = head[u];
	head[u] = cnt++;
}

void makeset() {
	for(int i = 0; i <= F; ++i)
		root[i] = i;
}

int findset(int x) {
	if(x != root[x])
		root[x] = findset(root[x]);
	return root[x];
}

void unin(int x,int y) {
	root[x] = y;
}

void dfs(int u,int id) {
	int i;

	for(i = head[u]; i != -1; i = e[i].nxt) {
		int v = e[i].v;
		if(e[i].id != id) {
			if(!vis[v]) {
				vis[v] = true;
				step[v] = low[v] = (++num);
				dfs(v, e[i].id);
				low[u] = MIN(low[u], low[v]);
			}
			else
				low[u] = MIN(low[u],step[v]);
			if(low[v] > step[u]) {
				bridge[bri_num][0] = u;
				bridge[bri_num++][1] = v;
			}
			else{
				int x = findset(u);
				int y = findset(v);
				if(x != y)
					unin(x,y);
			}
		}
	}
}

int num_cnt() {
	int i;
	int cnt_num;

	dfs(1, -1);
	cnt_num = 0;
	for(i = 1; i <= F; ++i) {
		int x = findset(i);
		if(tree_node[x] == -1)
			tree_node[x] = cnt_num++;
		else
			tree_node[i] = tree_node[x];
	}
	return cnt_num;
}

void init() {
	int i, u, v;

	clr(head, -1);
	clr(vis, false);
	clr(tree_node, -1);
	clr(d, 0);
	makeset();
	for(i = 0; i != R; ++i) {
		scanf("%d %d", &u, &v);
		addedge(u, v, i);
		addedge(v, u, i);
	}
	step[1] = low[1] = num = 1;
	cnt = bri_num = node = 0;
	vis[1] = true;
}

int main() {
	int i, u, v;

	while(scanf("%d%d",&F,&R) != EOF) {
		init();
		int cnt_num = num_cnt();
		for(i = 0; i < bri_num; ++i) {
			u = bridge[i][0];
			v = bridge[i][1];
			d[tree_node[u]]++;
			d[tree_node[v]]++;
		}
		for(i = 0; i < cnt_num; ++i)
			if(d[i] == 1)
				node++;
		printf("%d\n", (node + 1) / 2);
	}
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值