【树形DP】蓝桥杯 ALGO-4 结点选择

问题描述

有一棵 n 个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?

输入格式

第一行包含一个整数 n 。

接下来的一行包含 n 个正整数,第 i 个正整数代表点 i 的权值。

接下来一共 n-1 行,每行描述树上的一条边。

输出格式
输出一个整数,代表选出的点的权值和的最大值。
样例输入
5
1 2 3 4 5
1 2
1 3
2 4
2 5
样例输出
12
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定

对于20%的数据, n <= 20。

对于50%的数据, n <= 1000。

对于100%的数据, n <= 100000。

权值均为不超过1000的正整数。


【题意】
      遍历整棵树,求选出的点的权值和的最大值。
 【类型】
  树形动态规划
     【算法思路】
  本题类型是一道树形动态规划,这里在DFS中选择一个叶子节点开始自下向上进行运算,是因为如果按常规的想法若是从树的根开始向下递归  但是这个时候就要判断他的两个子节点是否被选上。 相反,若以叶子节点开始,就不必判断两个点了,直接判断他的父亲节点选还是不选,。为使得值最大,若从叶子节点开始效率更高一些。   

 对于每一个节点只有两种可能,即是选上或者是不选上,所以就建立一个二维数组,比如dp[N][2], 这里N表示节点数目,而后面2表示每个节点就两种选择,所以dp[i][0]表示第i个节点不选时的权值,而dp[i][1]表示第i个节点选上时的权值,便可以开始逆推了

其中,dfs(int u,int f)分别表示起始节点u和某一父节点f,f节点时让DFS终止的标志位节点。

#include <iostream>
#include<vector>
#include<cstring> 
#include<cstdio>
using namespace std;

const int nmax=10001;
vector<int>G[nmax];//邻接表 
int dp[nmax][2];//v[i][0]与v[i][1] 表示第i个节点选或者不选的时候所对应的value 
  //通过遍历整棵树,计算符合条件的value值 
int dfs(int u,int f){//v为u点的父节点 k为u点的子节点,保证子父不同
	for(int i=0;i<G[u].size();i++){
		
		int k=G[u][i];//从u节点开始的第i个节点
		if(k!=f){//若没搜索到尽头 
			dfs(k,u);//从子节点倒推,从下到上 
			dp[u][1]+=dp[k][0];//若选父节点,则子节点未选,更新已选的父节点权值
			dp[u][0]+=max(dp[k][0],dp[k][1]);//若父节点未选,则节点有两种可能,更新未选的父节点权值
		} 
	}
	
}
int main(int argc, char** argv) {
	int n;//节点数目
	int value;//每个节点对应的权值 
	memset(dp,0,sizeof(dp));
	
	cin>>n;
	for(int i=1;i<=n;i++){
		//scanf("%d",&value); 
		cin>>value;
		dp[i][1]=value;//初始化节点的权值 
	} 
    
    //初始化邻接表,建立父节点与子节点之间的边
	for(int i=1;i<n;i++){
		int u,v;//边的起点与终点;
		//scanf("%d%d",&u,&v);
		cin>>u>>v;
		G[u].push_back(v); 
		G[v].push_back(u);
	} 
	int ans;
	/*for(int i=0;i<n;i++){//纵向遍历邻接表 
		
		if(G[i].size()==1){//若为叶节点 
			dfs(i,-1);//设-1时所有节点的祖先节点 
			ans=max(dp[i][0],dp[i][1]);
			cout<<ans<<endl;
			break;
		}
	}*/
	dfs(1,-1);
     ans=max(dp[1][1],dp[1][0]); 
	cout<<ans<<endl;
	
	
	return 0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值