【Codeforces】【Gym - 101234I】Tree Game【分类枚举】

题意:

      给定一颗树,每条边初始均为白色。任意选取两个叶子节点,要求这两个叶子节点之间的边全部为白色,然后将这两个叶子节点之间的边全部染成黑色。直到无法再染色时停止,输出最少操作的次数。

思路:

      一开始看到这道题,以为是一个找规律或者贪心题,然后再加上全场没几个人过这道题,就放弃了,没有仔细想这道题。

      赛后才知道这其实就是一个暴力枚举多种情况的题目,还是有些可惜的。

      这题并不是一个贪心题,没有对点度数、点重儿子大小、点大小等常见指标进行贪心,只需要将情况进行分类,然后进行一一处理,下面是题目的分析。

      本题的基本思路是,任选一个度数大于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;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82284735
今日推荐