POJ3177_Redundant_Paths_边双连通分量_tarjan

题意:

给一个图,问你最少添加多少条边可以成为一个双连通图(就是去掉任何一条边后图仍然连通)

题解:【摘自北大的集训课件】

只需在求出所有的桥以后,把桥边删除,原图变成了多个连通块,则每个连通块就是一个边双连通分支。桥不属于任何一个边双连通分支,其余的边和每个顶点都属于且只属于一个边双连通分支。

可以求出所有的桥,把桥删掉。然后把所有的连通分支求出来,显然这些连通分支就是原图中的双连通分支。把它们缩成点,然后添上刚才删去的桥,就构成了一棵树。在树上添边使得树变成一个双连通分支即可。

本题只要求输出一共需要添加多少条边,而不需要求具体的方案。其实可以统计度为1的叶子节点(设共有x个),然后直接输出(x+1)/2即可

命题:一棵有n(n>=2)个叶子结点的树,至少(只需)要添加ceil(n/2)条边,才(就)能转变为一个没有桥的图。或者说,使得图中每条边,都至少在一个环上。
证明:
这里只证明n为偶数的情况。n为奇数的证明类似。
先证明添加n/2条边一定可以达成目标。
n=2时,显然只需将这两个叶子间连一条边即可。命题成立。
设n=2k(k>=1)时命题成立,即AddNum(2k)=k。下面将推出n=2(k+1)时命题亦成立
n=2k+2时,选取树中一条迹(无重复点的路径),设其端点为a,b;并设离a最近的度>=3的点为a',同理设b'。
(关于a‘和b’的存在性问题:由于a和b的度都为1,因此树中其它的树枝必然从迹<a,b>之间的某些点引出。否则整棵树就是迹<a,b>,n=2<2k+2,不可能。)

a’ b’不重合时:
在a,b间添一条边,则迹<a,b>上的所有边都已不再是桥。这时,将刚才添加的边,以及aa‘之间,bb’之间的边都删去,得到一棵新的树。因为删去的那些边都已经符合条件了,所以在之后的构造中不需要考虑它们。由于之前a‘和b’的度>=3,所以删除操作不会使他们变成叶子。因此新的树必然比原树少了两个叶子a,b,共有2k个叶子。由归纳知需要再加k条边。因此对n=2k+2的树,一共要添加k+1条边。

a’ b’重合时:
将a和一个非b的叶子节点x连上,然后将环缩点至a’。
因为叶子节点是偶数,所以必然还存在一个非b非x的叶子节点不在环上,因此a’不会变成叶子节点,于是新图比原图少2个叶子节点。

再证明n/2是最小的解。
显然,只有一个叶子结点被新加的边覆盖到,才有可能使与它相接的那条边进入一个环中。而一次加边至多覆盖2个叶子。因此n个叶子至少要加n/2条边。
证毕。


【PS:其实本题的数据比较弱,我是按照一种很简化的方法做的,即默认low值相同的点属于同一连通分量,low值不同的两点间的边就是割边,但是其实是不等价的,POJ的discuss里面有人讨论到一个图就是反例。不过此题数据比较水,就这么给水过了】

原题:

Redundant Paths
Time Limit: 1000MS
Memory Limit: 65536K
Total Submissions: 7302
Accepted: 3184

Description

In order to get from one of the F (1 <= F <= 5,000) grazing fields (which are numbered 1..F) to another field, Bessie and the rest of the herd are forced to cross near the Tree of Rotten Apples. The cows are now tired of often being forced to take a particular path and want to build some new paths so that they will always have a choice of at least two separate routes between any pair of fields. They currently have at least one route between each pair of fields and want to have at least two. Of course, they can only travel on Official Paths when they move from one field to another.

Given a description of the current set of R (F-1 <= R <= 10,000) paths that each connect exactly two different fields, determine the minimum number of new paths (each of which connects exactly two fields) that must be built so that there are at least two separate routes between any pair of fields. Routes are considered separate if they use none of the same paths, even if they visit the same intermediate field along the way.

There might already be more than one paths between the same pair of fields, and you may also build a new path that connects the same fields as some other path.

Input

Line 1: Two space-separated integers: F and R

Lines 2..R+1: Each line contains two space-separated integers which are the fields at the endpoints of some path.

Output

Line 1: A single integer that is the number of new paths that must be built.

Sample Input

7 7
1 2
2 3
3 4
2 5
4 5
5 6
5 7

Sample Output

2

Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time
11810196chengtbf3177Accepted320K0MSC++1540B2013-07-18 19:45:40


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define N 5005
using namespace std;

int dfn[N],low[N];
int dfs_time,visited[N];
int deg[N];//储存每个缩点的度数,缩点是将一个连通分量看作一个结点
vector<int>f[N];

int find_min(int a,int b)
{
	return a<b?a:b;
}

void tarjan(int u,int father)
{
	visited[u]=1;
	dfn[u]=low[u]=++dfs_time;
	int i,child;
	for ( i = 0; i < f[u].size(); i++)
	{
		child=f[u][i];
		if (!visited[child])
		{
			tarjan(child,u);
			low[u]=find_min(low[u],low[child]);
		}
		else if (child!=father)
		{
			low[u]=find_min(low[u],dfn[child]);
		}
	}
}

int main()
{
	int n,r,i,j,a,b,flag;
	int ans,num_deg_1;
	int u,v;
	while (scanf("%d%d",&n,&r)!=EOF)
	{
		for ( i = 0; i <=n ; i++)
		{
			f[i].clear();
		}
		memset(visited,0,sizeof(visited));
		memset(deg,0,sizeof(deg));
		dfs_time=0;
		ans=0;
		num_deg_1=0;
		for ( i = 1; i <= r; i++)
		{
			scanf("%d%d",&a,&b);
			flag=1;
			for ( j = 0; j < f[a].size(); j++)//考虑是否有重边,如果有的话,就不存进vector中
			{
				if (f[a][j]==b)
				{
					flag=0;
					break;
				}
			}
			if (flag)
			{
				f[a].push_back(b);
				f[b].push_back(a);
			}
		}
		tarjan(1,-1);
		for ( i = 1; i <=n ; i++)
		{
			u=i;
			for ( j = 0; j <f[i].size() ; j++)
			{
				v=f[i][j];
				if (low[u]!=low[v])
				{
					deg[low[u]]++;
					deg[low[v]]++;
				}
			}
		}
		for ( i = 1; i <= n; i++)
		{
			if (deg[i]==2)
			{
				num_deg_1++;
			}
		}
		ans=(num_deg_1+1)/2;
		printf("%d\n",ans);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值