算法训练 结点选择

算法训练 结点选择

时间限制:1.0s   内存限制:256.0MB

问题描述

有一棵 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+动态规划解题

由题意,采用领接表(记住head数组初始化)存储树的所有边,每个节点当做根节点再连接其他与之相连的节点,最后组合为一棵树。设置dp[ i ][ j ]表示第 i 个节点的最大权值(j=0表示不选,j=1表示选)

可得状态转移方程:

①dp[ i ][ 1 ] += dp[ j ][ 0 ]; 表示第 i 个节点选,则第j个节点( i 的相邻节点)不选

②dp[ i ][ 0 ] += max(dp[ j ][ 1 ], dp[ j ][ 0 ]); 表示第 i 个节点不选,那么第 j 个节点可选可不选,找最大值相加

DFS搜索直到叶节点,然后依次往上回溯计算最大权值。

注意:因为是给的无向的边,所以可能出现某节点的下一个节点是其父节点,遇到此节点跳过即可。

该题JAVA超时(过50%),可采用同样的思路用C++等语言通过。

import java.util.Arrays;
import java.util.Scanner;
public class Main {
	static final int N = 100005;
	static int cnt = 0;
	
	static int[][] dp = new int[N][2];
	static Node[] node = new Node[N*2];
	static int[] head = new int[N];
	
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		
		Arrays.fill(head, 0, head.length, -1);
		int n = cin.nextInt();
		
		for(int i=1; i<=n; i++) {
			dp[i][1] = cin.nextInt(); //第i个节点被选
		}
		for(int i=1; i<n; i++) {
			int u = cin.nextInt();
			int v = cin.nextInt();
			Add(u, v);
			Add(v, u);
		}
		
		Dfs(1, -1);
		int ans = Math.max(dp[1][0], dp[1][1]);
		System.out.println(ans);
		
		cin.close();
	}
	
	private static void Dfs(int u, int pre) {
		for(int i=head[u]; i!=-1; i=node[i].nex) {
			int v = node[i].to;
			if(v == pre) continue;
			
			Dfs(v, u);
			dp[u][1] += dp[v][0]; //选根节点
			dp[u][0] += Math.max(dp[v][0], dp[v][1]); //不选
		}
	}

	private static void Add(int v, int u) {
		node[cnt] = new Node();
		node[cnt].to = u; //边的终点
		node[cnt].nex = head[v]; //指向与v相连的另(/上)一条边的编号
		head[v] = cnt++; //记录v点在领接表的纵向位置
	}

	static class Node{
		int to;
		int nex;
	}
}

如有错误或不合理的地方,敬请指正~

加油!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值