树(tree) --中山市2009市选第四题

【问题描述】

图论中的树为一个无环的无向图。给定一棵树,每个节点有一盏指示灯和一个按钮。如果节点的按扭被按了,那么该节点的灯会从熄灭变为点亮(当按之前是熄灭的),或者从点亮到熄灭(当按之前是点亮的)。并且该节点的直接邻居也发生同样的变化。

开始的时候,所有的指示灯都是熄灭的。请编程计算最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。

【输入格式】

输入文件有多组数据。

输入第一行包含一个整数n,表示树的节点数目。每个节点的编号从1到n。 

输入接下来的n – 1行,每一行包含两个整数x,y,表示节点x和y之间有一条无向边。

当输入n为0时,表示输入结束。

【输出格式】

对于每组数据,输出最少要按多少次按钮,才能让所有节点的指示灯变为点亮状态。每一组数据独占一行。

【输入样例】

3

1 2

1 3

0

【输出样例】

1

【数据规模】

对于20%的数据,满足1 <= n <=15。

对于40%的数据,满足1 <= n <=50。

对于100%的数据,满足1 <= n <=100。

【解析】

又是一道树形DP啊.. 也是题表最后一题了.. 刷完就刷非题表的题目啦..

这道题作为市选的最后一道题也是挺难的.. 所以说在状态定义下需要一定的时间0.0.

本题与前几题(皇宫看守、战略游戏、没有上司的晚会)类似但要更加难一些,看下面这个例子(1表示有人,0表示没人):

1——0——1

这一个状态在前几题都是能够实现的,但这一题这种情况是不能实现的,以为两头开灯中间的点一开一关也就关了..

那么我们把状态定义如下,在点i中:

f[i][0]表示点i 没开 的最小花费

f[i][1]表示点i 没开 不亮 的最小花费

f[i][2]表示点i 开了 的最小花费

f[i][3]表示点i 开了 不亮 的最小花费 #尽管这种状态还是存在,但是有意义么,你没事闲着无聊把本来开着的关了有意思么..

当然这个的前提同样是以i为根的树,i的孩子都是亮的情况下所定义的状态。

现在就重点讲一下怎样遍历这三种状态..

void Tree_DP ( int x, int fa ){
	int i, j, k, f0, f1, f2;
	f0 = f1 = 0; f2 = 1;
	int minm = 999, minp = 999, sum = 0;
	for ( k = first[x]; k; k = a[k].next ){
		int y = a[k].y;
		if ( y == fa ) continue;
		Tree_DP ( y, x );
		if ( f[y][2] < f[y][0] ){
			f0 += f[y][2]; f1 += f[y][2];
			sum ++;
			minm = _min ( minm, f[y][0] - f[y][2] );
		}
		else {
			f0 += f[y][0]; f1 += f[y][0];
			minp = _min ( minp, f[y][2] - f[y][0] );
		}
		f2 += f[y][1];
	}
	if ( sum % 2 == 0 ) f0 += _min ( minm, minp );
	else f1 += _min ( minm, minp );
	f[x][0] = f0;
	f[x][1] = f1;
	f[x][2] = f2;
}
因为最多也就100个点..所以999足够算是最大了..

首先讲讲最简单的 开了亮了 这个状态的遍历,因为要使点i 开了并且亮了,那么最小花费就肯定是孩子节点 不开也不亮..

那么剩下两种我就一起讲吧..

要使点i 不开但亮  那么孩子节点中至少有一个要开灯并且开灯的孩子节点数目必须是奇数

要使点i 不开不亮  那么孩子节点中要么就都不开,要么开灯的孩子节点数目必须是偶数

那么问题来了,你怎么去找这个的最小花费呢?

首先我就把所有的孩子节点 不开但亮 和 开了亮了 两种状态之间的最小值全部加起来

计算由 不开但亮开了亮了 的最小增加值,记为minm

计算由 开了亮了 不开但亮 的最小增加值,记为minp

同时我也累加选择 开了亮了 的数量,记为sum

最后就可以对sum进行判断

假如sum是奇数,那么就满足了点i 不开但亮 的条件,所以就要在 不开不亮 上加上 minm和minp的最小值

假如sum是偶数,那么就满足了点i 不开不亮 的条件,所以就要在 不开但亮 上加上 minm和minp的最小值。

大概就是这样了..

【完整代码】

#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
struct node {
	int x, y, next;
}a[210]; int first[110], len, n;
void ins ( int x, int y ){
	len ++;
	a[len].x = x; a[len].y = y;
	a[len].next = first[x]; first[x] = len;
}
int f[110][3];
int _min ( int x, int y ){ return x < y ? x : y; }
void Tree_DP ( int x, int fa ){
	int i, j, k, f0, f1, f2;
	f0 = f1 = 0; f2 = 1;
	int minm = 999, minp = 999, sum = 0;
	for ( k = first[x]; k; k = a[k].next ){
		int y = a[k].y;
		if ( y == fa ) continue;
		Tree_DP ( y, x );
		if ( f[y][2] < f[y][0] ){
			f0 += f[y][2]; f1 += f[y][2];
			sum ++;
			minm = _min ( minm, f[y][0] - f[y][2] );
		}
		else {
			f0 += f[y][0]; f1 += f[y][0];
			minp = _min ( minp, f[y][2] - f[y][0] );
		}
		f2 += f[y][1];
	}
	if ( sum % 2 == 0 ) f0 += _min ( minm, minp );
	else f1 += _min ( minm, minp );
	f[x][0] = f0;
	f[x][1] = f1;
	f[x][2] = f2;
}
bool v[110];
int main (){
	int i, j, k, x, y;
	while ( scanf ( "%d", &n ) && n != 0 ){
		len = 0; memset ( first, 0, sizeof (first) );
		memset ( v, false, sizeof (v) );
		for ( i = 1; i < n; i ++ ){
			scanf ( "%d%d", &x, &y );
			ins ( x, y ); ins ( y, x );
			v[y] = true;
		}
		int st;
		for ( i = 1; i <= n; i ++ ){
			if ( v[i] == false ){ st = i; break; }
		}
		Tree_DP ( st, 0 );
		printf ( "%d\n", _min ( f[st][0], f[st][2] ) );
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值