问题描述
有一棵 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的正整数。
import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class 节点选择 { // 邻接表 static List<Integer>[] list; static int[][] dp; static int[] vaule; @SuppressWarnings({ "unchecked", "resource" }) public static void main(String[] args) { // TODO Auto-generated method stub Scanner sc = new Scanner(System.in); int n = sc.nextInt(); // 存储权值 dp = new int[n + 1][2]; vaule = new int[n]; for (int i = 0; i < n; i++) { vaule[i] = sc.nextInt(); } // dp[i][j]表示i节点,如果j=0表示不选择该节点,j=1表示选择 // 要最大值,那么dp[i][1]=dp[所有子节点][0]+value[i]; // dp[i][0]=max(dp[所有子节点][0],dp[所有子节点][1]); // 添加数据进树 list = new ArrayList[n]; for (int i = 0; i < n; i++) { list[i] = new ArrayList<>(); } // n-1次即可,list[]中第一个表示其父节点 for (int i = 0; i < n - 1; i++) { int c = sc.nextInt(); int d = sc.nextInt(); // 下标减1,如输入1,2那么0(1-1)节点加上2,1(2-1)节点加上1 list[c - 1].add(d - 1); list[d - 1].add(c - 1); } // 从根节点开始 dfs(0, -1); System.out.print(Math.max(dp[0][0], dp[0][1])); } // 搜索出最大的相加方法 public static void dfs(int x, int y) { // 遍历这个节点下的子节点 for (int i = 0; i < list[x].size(); i++) { // 获取子节点 int num = list[x].get(i); // 不是父节点时 if (num != y) { // 继续遍历x节点下的子节点 dfs(num, x); // 分开2种情况 dp[x][0] += Math.max(dp[num][0], dp[num][1]); // 这里不能加上自己的权值,因为还要判断上一个,只加上 dp[x][1] += dp[num][0]; } } // 遍历到最后的节点肯定是加上自己的值,再回上一层选择 dp[x][1] += +vaule[x]; } }