POJ3177 Redundant Paths边双连通分量

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

题意:有F个牧场(1<=F<=5000),现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。给定现有的R条(F-1<=R<=10000)直接连接两个牧场的路,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点。

分析:题目可转换为对于给定的无向连通图,至少添加几条边使它变成一个边双连通图。用tarjan算法求出所有的边连通分量,并将其缩成点构成一棵树,要想让这些缩点之间有至少两条不共边的路径,只需将所有叶节点(度为1的节点)连接起来即可,至少要连(叶节点+1)/2条边。


#include<iostream>
#include<algorithm>

using namespace std;
const int maxn = 5005, maxm = 10005;
int cnt, n, m, e, index,top;//cnt记录边连通分量序号,index记录遍历序列
int first[maxn], dfn[maxn], low[maxn];
int belong[maxn], out[maxn],stack[maxn];
bool inStack[maxn];


struct Edge {
	int u, v, next;
	Edge(){}
	Edge(int u,int v,int next):u(u),v(v),next(next){}
}edges[maxm*2];

void init()
{
	memset(first, -1, sizeof(first));
	e = index = cnt = top = 0;
}
void addEdge(int u, int v)
{
	edges[e] = Edge(u, v, first[u]);
	first[u] = e++;
	edges[e] = Edge(v, u, first[v]);
	first[v] = e++;
}

void tarjan(int u,int fa)
{
	inStack[u] = true;
	stack[top++] = u;
	dfn[u] = low[u] = ++index;
	bool flag = true;
	for (int i = first[u]; i != -1; i = edges[i].next)
	{
		int v = edges[i].v;
		if (v == fa&&flag)//判断是否为重边,第一次跳过,之后的要走
		{
			flag = false;
			continue;
		}
		if (!dfn[v])
		{
			tarjan(v, u);
			low[u] = min(low[u], low[v]);
		}
		else if (inStack[v])
			low[u] = min(low[u], dfn[v]);
	}
	if (low[u] == dfn[u])
	{
		cnt++;
		int v;
		do {
			v = stack[--top];
			belong[v] = cnt;
			inStack[v] = false;
		} while (v != u);
	}
}
int main()
{
	while (~scanf("%d%d", &n, &m))
	{
		init();
		for (int i = 0; i < m; i++)
		{
			int u, v;
			scanf("%d%d", &u, &v);
			addEdge(u, v);
		}

		for (int i = 1; i <= n; i++)
			if (!dfn[i])
				tarjan(i,-1);

		for (int i = 0; i < e; i++)
		{
			Edge p = edges[i];
			if (belong[p.u] != belong[p.v])//计算出度
				out[belong[p.u]]++;		
		}
		int  leaves=0;
		for (int i = 1; i <= cnt; i++)
			if (out[i]==1)
			{
				leaves++;
			}
		cout << (leaves + 1) / 2 << endl;
	}
	return 0;
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值