牛客小白月赛12(i)华华和月月逛公园

版权声明:随意转载 https://blog.csdn.net/nuiniu/article/details/88553768

题目:https://ac.nowcoder.com/acm/contest/392/I

首先先了解一下什么是割边割点!!!

割边:在无向图中,如果删除图中的一条边,图的连通分量增加,那就称该边为割边。(割边可以有多个)

割点:同理,在无向图中如果删除图中的一个顶点及顶点的邻边,图的连通分量增加,则称该点为割点。(割点也可以有多个)

方法:tarjin算法找割边的数量ans(割边是一定要走的边),所以不一定要走的边的数量为m-ans。

        这里,解释一下为什么要找割边,(画个图会很清楚的看出来)就拿题目的样例(如下)来说,{1,2,3,4,5}构成了个连通分量,如果我删除1to2或者2to3这个边,那么连通分量会变成{1},{2,3,4,5}或者{1,2},{3,4,5},与此同时,会导致所有的景点无法完全经过,因为割边是连接两端连通分量的唯一边!!!

5 5
1 2
2 3
3 4
4 5
3 5

#include<iostream>
#include<list>

using namespace std;

const int maxn=100005;

int low[maxn],dfn[maxn],ans,p; //dfn数组记录第一次经过这个顶点(下标)的时间戳,low记录的是可以经过这个顶点的最小时间戳(会在递归后进行更新得到最小的时间戳),ans记录割边的数量,p为当前时间戳
list<int> g[maxn];
list<int> sta;                  //没啥用。

void dfs(int u,int fa)      //此处的dfs即为tarjin算法的遍历方法
{
	low[u]=dfn[u]=++p;      //初始化两个时间戳数组
//    cout<<u<<endl;

	for(list<int >::iterator i=g[u].begin();i!=g[u].end();i++)
	{
	    int v=*i;
	    if(v==fa) continue;
		if(!low[v])
		{
			dfs(v,u);
			low[u]=min(low[v],low[u]);  //递归后更新当前点的最小时间戳,将dfs尽头的顶点的low传递下来,表示当前顶点属于某个连通分量

			if(low[v]>dfn[u]) ans++;   //记录割边的数量,如果' > '改成' >= '则ans是割点数量+1
		}
		else if(dfn[v]) low[u]=min(low[u],low[v]);  //遍历到顶点v,如果v已经遍历过的,则取v的low时间戳给u
	}
//	cout<<endl;
}

int main()
{
	int n,m;

	cin>>n>>m;

	for(int i=0;i<m;i++)
	{
		int u,v;

		cin>>u>>v;

		g[u].push_back(v);
		g[v].push_back(u);
	}

	dfs(1,0);

//   for(int i=1;i<=n;i++) cout<<low[i]<<' '<<dfn[i]<<endl;
    cout<<m-ans<<endl;

	return 0;
}

猜你喜欢

转载自blog.csdn.net/nuiniu/article/details/88553768