CF1029E Tree with Small Distances

CF1029E Tree with Small Distances

洛谷传送门

题目描述

给定一颗有根树(根节点为 111)。要求往树中加入一些边使得从根节点到其他节点的距离至多是 222。 求加入边的最小数量。(边全部都是无向的)

输入格式

第一行一个整数 nnn,表示树中的节点个数。 接下来 n−1n−1n−1 行,每行两个整数 x,yx,yx,y,表示 x,yx,yx,y 之间有一条连边。

输出格式

一个整数,表示加入边的最小数量。


题解:

看到最优化想DP,没想出来。正解是贪心。

肯定深度深的最不好伺候,先把深度深的搞定了之后,一层一层再逼近根节点这么搞,肯定是更优秀的。

那怎么伺候深度深的呢?

假设现在深度深的点为(x),如果直接连(x),肯定不划算的。我们如果连上他的父亲(f),那么肯定能使得更多的点符合条件。

于是贪心原则就出来了。感性认知是对的。

具体实现就是用堆来维护一个二元组,先处理深度打的节点,每次取出堆顶节点就打一圈标记。就解决了。

代码:

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=2e5+5;
int n,ans;
int tot,head[maxn],nxt[maxn<<1],to[maxn<<1];
int deep[maxn],fa[maxn];
bool v[maxn];
priority_queue<pair<int,int> >q;
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs(int x,int f)
{
    deep[x]=deep[f]+1;
    fa[x]=f;
    if(deep[x]>2)
        q.push(make_pair(deep[x],x));
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==f)
            continue;
        dfs(y,x);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    deep[0]=-1;
    dfs(1,0);
    while(!q.empty())
    {
        int x=q.top().second;
        q.pop();
        if(v[x])
            continue;
        int f=fa[x];
        v[f]=1;
        for(int i=head[f];i;i=nxt[i])
        {
            int y=to[i];
            v[y]=1;
        }
        ans++;
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/14015240.html