CF E .Tree with Small Distances(树上的贪心)

题意

这是一颗有n-1条边的无向树 , 在树上加最少的边使树的1节点到其他节点的距离最多为 2 ;

分析:很容易考虑的贪心的做法,但是该如何的贪心呢 ? 我一开始是打算贪心节点的儿子最多那一个 , 但这样是不行的,举个反例子例:1-2 ; 2-3 ; 3-4 ; 3-5 ; 3-6 ; 3-7 ; 4-8 ; 4-9 ; 5-10 ; 5-11 ; 6-12 ; 6-13 ; 7-14 ; 7-15 ; 可自己做出图,按这样的贪心出来的结果是5 ; 结果是4,所以这样的贪心是不行的 ; 我们在考虑其他的贪心做法;

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

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

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

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

为什么呢? 我们是贪心出距离最长的节点优先考虑连边,故是从下肉上递推 ,而最长的节点一定是树的根节点,故连接该节点的父亲节点所得出来的贡献更大:

#include<bits/stdc++.h>
using namespace std;
int n ;
priority_queue<pair<int , int > > q;
vector<int>G[200001];
bool book[200001];
int per[200001],dis[200001];
void init( )
{
    for(int i=1 ; i<=n ; i++)
    book[i]=0;
    for(int i=1 ; i<=n ; i++)
    G[i].clear();
    while(q.size())
    q.pop();
}
void dfs(int cur , int fa)
{
    per[cur] = fa ;
    for(int i=0 ; i<G[cur].size() ; i++)
    {
        int v = G[cur][i] ;
        if(v==fa)
        continue ;
        dis[v] = dis[cur] + 1 ;
        if(dis[v]>2)
        q.push(make_pair(dis[v],v));
        else
        book[v]=1;
        dfs(v,cur);
    }
}

int main( )
{

    scanf("%d",&n);
    for(int i=1 ; i<=n-1 ; i++)
    {
        int u,v ;
        scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1,-1);
    int ans=0;
    while(q.size())
    {
        int v = q.top().second;q.pop();
        if(book[v]==1)
        continue;
        int fa=per[v];
        book[fa]=1;
        ans++;
        printf("%d %d\n",v,fa);
        for(int i=0 ; i<G[fa].size() ; i++)
        {
            int cur = G[fa][i];
            book[cur]=1;
        }
    }
    printf("%d\n",ans);
    return 0;
}
View Code

猜你喜欢

转载自www.cnblogs.com/shuaihui520/p/9542072.html