tree触宝科技2017秋季校招笔试后端大数据编程题

题目描述

   给定一棵节点数为n的有根树, 根节点编号为0. 给出1~n-1的每个节点的父亲节点以及其所拥有的value值. 对每个节点下的子树(包含该节点)的最大的value值与子树外其他节点最大的value值的差的绝对值进行求和(无须对根节点0求)。

输入描述

第一行输入n表示有n个节点 (0 < n <= 100000)
第二行n - 1个数, 表示1 ~ n - 1 每个节点的父亲节点的编号
第三行n个数, 表示每个节点上的value值 (0 <= value <= 1000)

输出描述

输出一个整数表示所有节点下要求的差的绝对值的和

解题思路

   基本思想:对于每一棵树,计算这棵树的最大值和所有其他节点构成的集合的最大值。
   可以选用set等可以自动排序的容器存储节点的值,为每一个节点定义一个集合,集合中不仅保存它本身的值,还包括所有后代的值,这样就形成了森林,对于森林里的每一棵树,将其与总的集合做差集,由于自动排序,可以直接得到两个集合的最大值,相减取绝对值进行累加。
   上面的方法在数据量很大时,其时间和空间的效率很差,原因在于很多数据冗余访问,对于一棵子树而言,其最大值,可以由所有子树最大值与根节点值进行比较得到,这可以由一个深度搜索得到。
   内部最大值可以看作是一个由小到大的扩张,而外部最大值则可以看作是一个由大到小的收缩。
外部最大值
   out_max是out_max,root->val以及该子树以外的子树的最大值比较得到,因此,为避免该子树的最大值是所有子树中最大的那一个,还需要记录一个次最大值,这样每一个节点都需要记录三个值,以该节点为根节点的树的最大值,该节点的所有子树的最大的两个值,因为整棵树的最大值包含根节点,而另外两个最大值则是不包含节点的所有子树的最大值。

代码实现

#include <iostream>
#include <vector>
#define N 100003
using namespace std;
vector<vector<int> > chs(N,vector<int>());
int vals[N];
int rec[N][3];
int n;
int ans=0;
int max(int a,int b){
	return a>b?a:b;
}
void dfs1(int node){
	rec[node][0]=vals[node];
	int lens=chs[node].size();
	for(int i=0;i<lens;i++){
		int c=chs[node][i];
		dfs1(c);
		rec[node][0]=max(rec[node][0],rec[c][0]);
		if(rec[c][0]>rec[node][1]){
			rec[node][2]=rec[node][1];
			rec[node][1]=rec[c][0];
		}else if(rec[c][0]>rec[node][2])
			rec[node][2]=rec[c][0];
	}
}
void dfs2(int node,int maxOut){
	ans+=abs(rec[node][0]-maxOut);
	maxOut=max(maxOut,vals[node]);
	int lens=chs[node].size();
	for(int i=0;i<lens;i++){
		int c=chs[node][i];
		if(rec[c][0]==rec[node][1])
			dfs2(c,max(maxOut,rec[node][2]));
		else
			dfs2(c,max(maxOut,rec[node][1]));
	}

}
int solve(){
	dfs1(0);
	dfs2(0,0);
	return ans-rec[0][0];
}
int main(){
	cin>>n;
	int parent;
	int i;
	for(i=1;i<n;i++){
		cin>>parent;
		chs[parent].push_back(i);
	}
	for(i=0;i<n;i++)
		cin>>vals[i];
	cout<<solve()<<endl;
}


发布了16 篇原创文章 · 获赞 0 · 访问量 343

猜你喜欢

转载自blog.csdn.net/qq_41922018/article/details/104251447