纪中暑假集训 2020.08.07【NOIP提高组】模拟 T2:【佛山市选2013】树环转换

【佛山市选2013】树环转换

Description

给定一棵N个节点的树,去掉这棵树的一条边需要消耗值1,为这个图的两个点加上一条边也需要消耗值1。树的节点编号从1开始。在这个问题中,你需要使用最小的消耗值(加边和删边操作)将这棵树转化为环,不允许有重边。

环的定义如下:

(1)该图有N个点,N条边。

(2)每个顶点的度数为2。

(3)任意两点是可达的。

树的定义如下:

(1)该图有N个点,N-1条边。

(2)任意两点是可达的。

Input

第一行是一个整数N代表节点的个数。

接下来N-1行每行有两个整数U, V(1 ≤ U, V ≤ N),表示双向边(U, V)

Output

输出把树转化为环的最小消耗值。

Sample Input

4

1 2

2 3

2 4

Sample Output

3

Data Constraint

对于20%的数据,有1≤N≤10。

对于100%的数据,有1≤N≤1000000。

反思&题解

比赛思路: 忘了……
正解思路: 我们考虑贪心的思想:去掉多的边补齐少的边,很显然最优肯定是他们一一对应,所以最后结果就是去掉的边*2再加上树比环少的那条边。对于一个叶子节点,我们就不用去掉这条边了;对于一个节点有两个及以上个儿子的,就要去掉只剩1条(根节点的话可以剩2条),不过如果它一个儿子被去掉过边,那这个儿子就不用算了。运用这种思想遍历一棵树统计答案就行了;
不过GMOJ的评测机会爆栈,所以要用人工栈或者BFS
(据说树形DP才是最正的正解,不过贪心能过为啥要DP呢)
反思: 对于树的各种性质还是要多了解

CODE

#include<bits/stdc++.h>
using namespace std;
struct arr
{
	int to,next;
}edge[2000005];
int n,head[2000005],cnt,ans,d[4000005],fa[2000005],tot[2000005];
bool bz[2000005],flag[2000005];
void add(int u,int v)
{
	edge[++cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
int bfs()
{
	int h=0,t=1;
	d[1]=1;
	bz[1]=true;
	while (h<t)
	{
		int i,u=d[++h];
		for (i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if (v!=fa[u])
			{
				d[++t]=v;
				fa[v]=u;	
			}
		}
	}
	int i;
	for (i=t;i>=1;i--)
		printf("%d\n",d[i]);
	for (i=t;i>=1;i--)
		if (tot[d[i]]<2) tot[fa[d[i]]]++;
	memset(d,0,sizeof(d));
	h=0;
	t=1;
	d[1]=1;
	while (h<t)
	{
		int u=d[++h],i;
		if (tot[u]>=2)
		{
			ans+=tot[u]-1;
			if (u==1) ans--;
		}
		for (i=head[u];i;i=edge[i].next)
		{
			int v=edge[i].to;
			if (v!=fa[u]) d[++t]=v;
		}	
	}
}
int main()
{
	scanf("%d",&n);
	int i;
	for (i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	bfs();
	printf("%d\n",ans*2+1);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/CMC_YXY/article/details/107880150