CF1029E

版权声明:本博客中有原创标签的内容均为博主原创,转载请注明出处! https://blog.csdn.net/lleozhang/article/details/83278229

一个看起来就不对的贪心居然是正解...

但仔细思考一下,这种贪心倒的确找不到反例..

贪心思想:每次找出离根节点最远的点,然后由根节点向这个点的父节点连边,一直连到所有点都能被覆盖即可,这样构造出的一定是一个可行的最优解

正确性证明(个人YY):

主要是要证明这种做法的最优性:

首先,由于所有点都要求被覆盖,自然离根节点最远的点也不例外

那么,如果想覆盖上离根节点最远的点,只会有两种覆盖方法:一种是将根节点与这个点本身相连,另一种是将根节点与这个点的父节点相连

不难发现,将根节点与这个点的父节点相连,这样的方案一定不会差

证明:假设这个父节点还有别的子节点,那么与父节点相连后这些子节点都能被覆盖,这样一定是更优的

而即使这个父节点没有别的子节点,他还有自己的父节点,这样连边也能减少根节点与他的父节点的距离,也会达到更好的效果

即使上面两点都没有起作用,至少这样还可以覆盖上最远的点,也并不会使代价增大,所以这样做是完全可行的。

这样就完事了

(ps:树上bfs真好用)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
using namespace std;
struct Edge
{
	int next;
	int to;
}edge[400005];
bool used[200005];
int head[200005];
int f[200005];
int n;
int cnt=1;
void init()
{
	memset(head,-1,sizeof(head));
	cnt=1;
}
void add(int l,int r)
{
	edge[cnt].next=head[l];
	edge[cnt].to=r;
	head[l]=cnt++;
}
struct node
{
	int num;
	int dep;
}p[200005];
void bfs(int rt)
{
	queue <int> M;
	p[rt].dep=0;
	p[rt].num=rt;
	M.push(rt);
	while(!M.empty())
	{
		int u=M.front();
		M.pop();
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int to=edge[i].to;
			if(p[to].dep)
			{
				continue;
			}
			p[to].dep=p[u].dep+1;
			p[to].num=to;
			f[to]=u;
			M.push(to);
			if(p[to].dep<=2)
			{
				used[to]=1;
			}
		}
	}
}
bool cmp(node a,node b)
{
	return a.dep>b.dep;
}
int main()
{	
	scanf("%d",&n);
	init();
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	bfs(1);
	sort(p+1,p+n+1,cmp);
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(used[p[i].num])
		{
			continue;
		}
		ans++;
		int u=f[p[i].num];
		used[u]=1;
		for(int j=head[u];j!=-1;j=edge[j].next)
		{
			int to=edge[j].to;
			used[to]=1;
		}
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lleozhang/article/details/83278229