题意:
给定一颗n个顶点的双向连通树,现在要在这颗树上加边,使得这颗树的顶点 1 到达其他顶点的距离 <= 2,问最少需要加几条边。
思路:
看到这道题之后,很自然的能够想到贪心,但是贪心的对象是什么需要确定。
比赛的时候我的思路是对这个节点的度数进行贪心,但是可以发现这个思路显然不对,然后就回去继续想D题了,所以究竟用什么来贪心是个技术性问题。
我们可以来思考一下,能拿什么来贪心,节点度数,节点距顶点1的距离,节点的儿子数,节点的父节点,总共也就这么几个指标,可以进行排除法。
因此可以发现度数、儿子数、节点数都不可以,因为没有办法满足当前最优,但是距离是可以的,因为离节点1越远的点越难通过加边到达,因此应该按照距离进行贪心,然后就又出现了一个问题。
很多人进行到这步之后,就会理所当然的认为找到距离最远的点,然后让节点1和该点连一条边,然后很自然地就wa了...
仔细思考一下,找到这个点之后,应该和谁连边,是和该节点的儿子、父亲还是节点本身,儿子显然不成立,如果和父亲连边显然比本身更优,因此题目就可以解决了。
注意:
此类问题难度不大,但是需要仔细想一想,不要被一些点迷惑了,可以采用把所有指标列出来,进行排除的操作!继续加油!!
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int M = 4*1e5+100;
const int N = 2*1e5+100;
struct Edge{
int to,next;
}e[M];
int head[N],book[N],tot,n,ans,dis[N],pre[N];
priority_queue<pair<int,int> >q;
void init()
{
tot = 1; ans = 0;
rep(i,1,n) head[i] = 0;
rep(i,1,n) book[i] = 0;
while(q.size()) q.pop();
}
void add(int x,int y)
{
e[++tot].to = y; e[tot].next = head[x]; head[x] = tot;
}
void dfs(int v,int fa)
{
pre[v] = fa;
for(int i = head[v]; i ; i = e[i].next)
{
int y = e[i].to;
if(y == fa) continue;
dis[y] = dis[v]+1;
if(dis[y] > 2) q.push(make_pair(dis[y],y));
else book[y] = 1;
dfs(y,v);
}
}
void solve()
{
while(q.size())
{
int v = q.top().second;
q.pop();
if(book[v] == 1) continue;
v = pre[v];
book[v] = 1;
ans++;
for(int i = head[v]; i ; i = e[i].next)
{
int y = e[i].to;
book[y] = 1;
}
}
printf("%d\n",ans);
}
int main()
{
while(~scanf("%d",&n))
{
init();
rep(i,1,n-1)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y); add(y,x);
}
dfs(1,0);
solve();
}
return 0;
}