题意:
给定一颗树,每条边初始均为白色。任意选取两个叶子节点,要求这两个叶子节点之间的边全部为白色,然后将这两个叶子节点之间的边全部染成黑色。直到无法再染色时停止,输出最少操作的次数。
思路:
一开始看到这道题,以为是一个找规律或者贪心题,然后再加上全场没几个人过这道题,就放弃了,没有仔细想这道题。
赛后才知道这其实就是一个暴力枚举多种情况的题目,还是有些可惜的。
这题并不是一个贪心题,没有对点度数、点重儿子大小、点大小等常见指标进行贪心,只需要将情况进行分类,然后进行一一处理,下面是题目的分析。
本题的基本思路是,任选一个度数大于1的点作为根节点,然后对于根节点进行dfs,每次dfs记录两个值,一个是t1,一个是t2.
t1用来表示当前节点的子节点中只能提供1个叶子的子节点个数,
t2用来表示当前节点的子节点中只能提供2个叶子的子节点个数。
我们需要先从小的结构入手【这是一个化繁为简的常见方法,需要深刻领悟】,如下图。
结构1:
此时对于节点A来说,t1 = 1,t2 = 0,对于这种情况的话,就继续往上找叶子节点;
结构2:
此时对于节点B来说,t1 = 2,t2 = 0;对于节点A来说,t1 = 1,t2 = 1;在这种情况下的话,对于节点A来说,其实A的儿子中只有一个叶子节点,因为这种情况只需要连一条边出去即可。
结构3:
对于这种情况,A的t2 = 2,则直接将两个节点合并,即画一条如图所示的边,然后ans++即可,此时A节点的节点个数变为0.
结构4:
对于这种情况,也直接进行合并,此时对于节点A来说,t1 = 1,t2 = 0.
相信这个时候,你已经明白了这道题大概的思路了。不难发现,对于每个节点来说,t1不会超过2,t2不会超过1,因此t1可以取0、1、2三种情况,t2可以取0、1两种情况,然后我们对于这两两组合的6种情况进行分类讨论即可。
剩下的内容就见代码了,代码不长,相信你们可以明白。
反思:
针对树的图论题中,有大量的思维题,不能每次都把思路局限在对于节点大小、节点深度贪心这种层次上,还要考虑是不是只是一个分类讨论,需要把思路拓宽。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 1e5+10;
const int M = 2*1e5+100;
struct Edge{
int to,next;
}e[M];
int n,tot,head[N],deg[N],ans;
void init()
{
tot = 1, ans = 0;
rep(i,0,n) head[i] = 0;
rep(i,0,n) deg[i] = 0;
}
void add(int x,int y)
{
e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}
int dfs(int x, int fa)
{
int t1 = 0, t2 = 0;
int cnt = 0;
for(int i = head[x]; i ; i = e[i].next)
{
int y = e[i].to;
if(y == fa) continue;
cnt++;
int tmp = dfs(y,x);
if(tmp == 1) t1++;
else if(tmp == 2) t2++;
}
if(!cnt) return 1;
while(t2 >= 2) {ans++; t2-=2; }
while(t1 >= 3) {ans++; t1-=2; }
if(t2 == 0)
{
if(t1 == 0) return 0;
else if(t1 == 1) return 1;
else if(t1 == 2) return 2;
}
else if(t2 == 1)
{
if(t1 == 0) return 2;
else if(t1 == 1) return 2;
else if(t1 == 2) {ans++; return 1;} //这里要注意
}
}
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);
deg[x]++; deg[y]++;
}
if(n == 2){
printf("1\n");
continue;
}
int x;
rep(i,1,n)
if(deg[i] > 1){
x = i;
break;
}
int h = dfs(x,-1);
if(h == 2) ans++;
printf("%d\n",ans);
}
return 0;
}