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

时间:2023-03-08 22:35:48

题意

这是一颗有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[];
bool book[];
int per[],dis[];
void init( )
{
for(int i= ; i<=n ; i++)
book[i]=;
for(int i= ; i<=n ; i++)
G[i].clear();
while(q.size())
q.pop();
}
void dfs(int cur , int fa)
{
per[cur] = fa ;
for(int i= ; i<G[cur].size() ; i++)
{
int v = G[cur][i] ;
if(v==fa)
continue ;
dis[v] = dis[cur] + ;
if(dis[v]>)
q.push(make_pair(dis[v],v));
else
book[v]=;
dfs(v,cur);
}
} int main( )
{ scanf("%d",&n);
for(int i= ; i<=n- ; i++)
{
int u,v ;
scanf("%d%d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
dfs(,-);
int ans=;
while(q.size())
{
int v = q.top().second;q.pop();
if(book[v]==)
continue;
int fa=per[v];
book[fa]=;
ans++;
printf("%d %d\n",v,fa);
for(int i= ; i<G[fa].size() ; i++)
{
int cur = G[fa][i];
book[cur]=;
}
}
printf("%d\n",ans);
return ;
}