zoukankan      html  css  js  c++  java
  • CF 1042F

    玄学贪心...

    题意:给出一棵树,要求将他的所有叶节点分成最少的组,且在每组中的任意两节点之间的距离不大于k

    解析:

    显然是个贪心啦...

    稍微考虑一下贪心思想:

    我们从下向上合并整棵树,在合并到某个节点时,我们把他以下的所有叶节点到他的距离全处理出来然后排序,设所有距离排序后为d1,d2...dn

    接下来,我们从大到小枚举每个d,如果满足di+di-1+2<=k,那么说明从1到i的所有子节点都可以分到一组里面,而剩下的节点只能单独一个分进一组里了

    然后我们向上返回不单独分组的点中距离最大的一个的距离,向上回溯合并即可

    画图理解一下:

    如图所示,这是一棵树,设k=2

    首先我们合并5,6两个点,那么发现这两个点到根的距离都是1,于是我们开心地将这两个点合并到一个集合里,然后回溯

    其余最底层节点同理

    这是合并前叶节点的状态

     这是合并上去第一层的状态

    继续合并一层:

    于是我们可以继续开心地合并,这时可以发现,所有的次二层节点都无法再合并,这时合并结束,一共需要三个集合

    可能这一点不是很好理解,所以我们再假设k=4,那么就可以把2和4合并起来,而3还是无法合并,这样就只需要两个集合了。

    总结一下思路:将一个根节点以下的所有叶节点到根节点的距离排序,然后倒序枚举,对于所有的di+di-1+2>=k的部分,我们都要把di单独分组,而剩余的部分可以分进一组,将这一组中的叶节点到这个根节点最长的距离回溯到上一层即可。

    证明贪心的正确性:

    首先,剩余部分可以分成一组是显而易见的,因为不会有比这更优的方案

    而其他的点需要单独分组的原因:假设这些点可以与另一部分子树中的某一部分合并成一个集合,那么根据距离的关系可知,剩下的分成一组的点一定也可以和那一部分合并成一个集合,但这两者却不能合为一个集合,所以无论怎么合并都至少需要两个集合,也就是这样分组一定是某一种最优方案!

    这样就完事了

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    #include <queue>
    #include <stack>
    #include <vector>
    using namespace std;
    struct Edge
    {
    	int next;
    	int to;
    }edge[2000005];
    int head[1000005];
    int inr[1000005];
    int cnt=1;
    int n,k;
    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++;
    }
    int cct=0;
    int dfs(int x,int fx)
    {
    	vector<int>v;
    	for(int i=head[x];i!=-1;i=edge[i].next)
    	{
    		int to=edge[i].to;
    		if(to==fx)
    		{
    			continue;
    		}
    		v.push_back(dfs(to,x)+1);
    	}
    	if(v.size()==0)
    	{
    		return 0;
    	}
    	sort(v.begin(),v.end());
    	int i;
    	for(i=v.size()-1;i>=1;i--)
    	{
    		if(v[i]+v[i-1]<=k)
    		{
    			break;
    		}
    		cct++;
    	}
    	return v[i];
    }
    int main()
    {
    	scanf("%d%d",&n,&k);
    	init();
    	for(int i=1;i<n;i++)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    		inr[x]++;
    		inr[y]++;
    	}
    	int rot=0;
    	for(int i=1;i<=n;i++)
    	{		
    		if(!rot&&inr[i]!=1)
    		{
    			rot=i;
    			break;
    		}
    	}
    	dfs(rot,rot);
    	printf("%d
    ",cct+1);
    	return 0;
    }
  • 相关阅读:
    React-Native到0.44版本后Navigator 不能用的问题
    php基础
    数据库学习内容复习
    数据库常用的函数
    45道题 数据库的
    数据库里any 和 all 的区别
    高级查询
    表中添加列,删除列,修改列名字
    创建,读取,修改,删除表 简单查询 12种
    设计表:多张表存储学生成绩及各种信息
  • 原文地址:https://www.cnblogs.com/zhangleo/p/10764174.html
Copyright © 2011-2022 走看看