节点选择
问题描述
有一棵 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的正整数。
Code
public class code03_choose {
public static void main(String[] args) {
new code03_choose().count();
}
public Scanner sc;
public int[][] weight;// 权值 [][0]表示不选择这个节点,[][1]表示选择这个节点
public List<List<Integer>> adj;// 邻接表
public void count() {
weight = new int[100001][2];// 下标从1开始
sc = new Scanner(new InputStreamReader(System.in));
while (sc.hasNext()) {
adj = new ArrayList<List<Integer>>();// 头指针的线性表
input();
dfs(1, 0);// 1根节点,0无前驱结点
System.out.println(Math.max(weight[1][0], weight[1][1]));
}
sc.close();
}
private void input() {
int n = sc.nextInt();// 结点数
adj.add(new ArrayList<Integer>());
for (int i = 1; i <= n; i++) {
weight[i][0] = 0;
weight[i][1] = sc.nextInt();
adj.add(new ArrayList<Integer>());// 创建头结点
}
int head, tail;// 弧的头尾
for (int i = 1; i < n; i++) {
tail = sc.nextInt();
head = sc.nextInt();
adj.get(tail).add(head);// 添加表结点
adj.get(head).add(tail);// 无向图,添加表结点
}
}
private void dfs(int root, int pre) {// pre为前驱结点
List<Integer> list = adj.get(root);// 当前链表
for (Integer i : list) {
if (i != pre) {
dfs(i, root);
// 不选root点
weight[root][0] += Math.max(weight[i][0], weight[i][1]);
// 选root点
weight[root][1] += weight[i][0];
}
}
}
}