第六届蓝桥杯C/C++第10题生命之树解题报告

题目:在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, ..., vk, b} 使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。


经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。但是由于 atm 不擅长计算,他不知道怎样有效的求评分。他需要你为他写一个程序来计算一棵树的分数。




「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。由于这是一棵树,所以是不存在环的。




「输出格式」
输出一行一个数,表示上帝给这棵树的分数。




「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5




「样例输出」
8




「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。




资源约定:
峰值内存消耗 < 256M

CPU消耗  < 3000ms


思路:

这是一很简单的树规,只要把握好根节点与下属子树的关系就可以出思路,然后用个DFS就暴力破掉,算法时间复杂度O(n),空间算不了管他咧,反正不会爆就是了;

首先我们来转换一下题目,他说了那么多,我们可以从中提炼出我们是在找这个树的一个权值最大的一个子树,也就是集合S且集合S不能为空;

好,现在目标已经确定,我们就来看看怎么找,首先,每一个树的子树都只有两种情况,要么是包含自身的根结点,要么是不包含根结点,那么父结点和下属子树有什么关系呢?再看看题目我们已经把它转化成找这个树的一个权值最大的子树,那么父结点和下属子树的关系不就是:“要么最大的是包含父结点的一颗下属最大子树,要么就是    不包含父结点的一颗下属最大子树;”

动态转移方程MaxTree=Max(connet_max,unconnet_max);

PS:树规转移方程不像普通动规一样可以直接套,他只是给你一种编程思路,比如MAX()怎么求这个MAX 树规方程不能完全写出来 这个要自己去实现它


基本思路讲完 直接上代码:

#include <iostream>
using namespace std;
const int Min= 0x0fffffff * -1;  //求INT的最小值 
inline int GetMax(int a,int b){ return a > b? a : b; } 

class Graph
{
public:
	class Vex;
	class Edg
	{
	public:
			Edg *next;
			Vex *to_node;
			Edg():next(NULL),to_node(NULL){}
	};
	class Vex
	{
	public:
			Edg *e_first;
			bool block;//判断是否被遍历过 
			int m_weight;//当前的结点权值 
			int c_max;//当前含根子树的最大权值 
			int unc_max;//当前不含根子树的最大权值 
			Vex():e_first(NULL),m_weight(0),c_max(0),unc_max(Min),block(false){}
			//构造邻接表 
			void Add(Vex *right) 
			{
				Edg *e=new Edg();
				e->next=e_first;
				e->to_node=right;
				e_first=e;
			}
			void Connet(Vex *right)
			{
				Add(right);
				right->Add(this);
			}
			
	};
	Vex *v;
	Graph(int n)
	{
		v=new Vex[n+1];
	}
	void DFS(Vex *v)
	{
		v->block=true;
		for(Edg *e=v->e_first;e;e=e->next)
		{
			if(e->to_node->block)
				continue;
			DFS(e->to_node);
			//状态转移方程 
			v->c_max += GetMax(e->to_node->c_max,0);//如果下属含根结点最大子树是正数 累加; 
			v->unc_max=GetMax(GetMax(e->to_node->c_max,e->to_node->unc_max),v->unc_max);/*下属最大子树有两种情况 含根或不含根  哪个大就把哪个													赋值给父结点*/ 
		}
		v->block=false;
	}
};


int main(int argc, char** argv) {
	int n,w,u,to;
	cin>>n;
	Graph G(n);
	for(int i=1;i<=n;++i)
	{
		cin>>w;
		G.v[i].m_weight=w;
		G.v[i].c_max=w;
	}
	for(int i=1;i<n;++i)
	{
		cin>>u>>to;
		G.v[u].Connet(G.v+to);
	}
	G.DFS(G.v+1);
	cout<<GetMax(G.v[1].c_max,G.v[1].unc_max);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值