题意:
这是一颗有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了...
仔细思考一下,找到这个点之后,应该和谁连边,是和该节点的儿子、父亲还是节点本身,儿子显然不成立,如果和父亲连边显然比本身更优,因此题目就可以解决了。
为什么呢? 我们是贪心出距离最长的节点优先考虑连边,故是从下肉上递推 ,而最长的节点一定是树的根节点,故连接该节点的父亲节点所得出来的贡献更大:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ",v,fa); for(int i=0 ; i<G[fa].size() ; i++) { int cur = G[fa][i]; book[cur]=1; } } printf("%d ",ans); return 0; }