【树上贪心】Tree with Small Distances【codeforces-Round #506-div3-E】

题意:

     给定一颗n个顶点的双向连通树,现在要在这颗树上加边,使得这颗树的顶点 1 到达其他顶点的距离 <= 2,问最少需要加几条边。

思路:

     看到这道题之后,很自然的能够想到贪心,但是贪心的对象是什么需要确定。

     比赛的时候我的思路是对这个节点的度数进行贪心,但是可以发现这个思路显然不对,然后就回去继续想D题了,所以究竟用什么来贪心是个技术性问题。

     我们可以来思考一下,能拿什么来贪心,节点度数,节点距顶点1的距离,节点的儿子数,节点的父节点,总共也就这么几个指标,可以进行排除法。

     因此可以发现度数、儿子数、节点数都不可以,因为没有办法满足当前最优,但是距离是可以的,因为离节点1越远的点越难通过加边到达,因此应该按照距离进行贪心,然后就又出现了一个问题。

     很多人进行到这步之后,就会理所当然的认为找到距离最远的点,然后让节点1和该点连一条边,然后很自然地就wa了...

     仔细思考一下,找到这个点之后,应该和谁连边,是和该节点的儿子、父亲还是节点本身,儿子显然不成立,如果和父亲连边显然比本身更优,因此题目就可以解决了。

注意:

此类问题难度不大,但是需要仔细想一想,不要被一些点迷惑了,可以采用把所有指标列出来,进行排除的操作!继续加油!!

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int M = 4*1e5+100;
const int N = 2*1e5+100;

struct Edge{
	int to,next;
}e[M];
int head[N],book[N],tot,n,ans,dis[N],pre[N];
priority_queue<pair<int,int> >q;

void init()
{
	tot = 1; ans = 0;
	rep(i,1,n) head[i] = 0;
	rep(i,1,n) book[i] = 0;
	while(q.size()) q.pop();
}

void add(int x,int y)
{
	e[++tot].to = y; e[tot].next = head[x]; head[x] = tot;
}

void dfs(int v,int fa)
{
	pre[v] = fa;
	for(int i = head[v]; i ; i = e[i].next)
	{
		int y = e[i].to;
		if(y == fa) continue;
		dis[y] = dis[v]+1; 
		if(dis[y] > 2) q.push(make_pair(dis[y],y));
		else book[y] = 1;
		dfs(y,v);
	}
}

void solve()
{
	while(q.size())
	{
		int v = q.top().second;
		q.pop();
		if(book[v] == 1) continue;
		v = pre[v];
		book[v] = 1;
		ans++;
		for(int i = head[v]; i ; i = e[i].next)
		{
			int y = e[i].to;
			book[y] = 1;
		}
	}
	printf("%d\n",ans);
}

int main()
{
	while(~scanf("%d",&n))
	{
		init();
		rep(i,1,n-1)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y); add(y,x);
		}
		dfs(1,0);
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82079503