WUSTOJ 1299: 结点选择(Java)
题目链接:🔗1299: 结点选择
参考:🔗【Java】 蓝桥杯ALGO-4 算法训练 结点选择——柳婼 の blog
Description
有一棵n
个节点的树,树上每个节点都有一个正整数权值。如果一个点被选择了,那么在树上和它相邻的点都不能被选择。求选出的点的权值和最大是多少?
Input
多组测试数据
每组第一行包含一个整数n
。
接下来的一行包含n
个正整数,第i
个正整数代表点i
的权值。
接下来一共n-1
行,每行描述树上的一条边。
Output
输出一个整数,代表选出的点的权值和的最大值。
Simple Input
5
1 2 3 4 5
1 2
1 3
2 4
2 5
Sample Output
12
Hint
样例说明
选择3、4、5号点,权值和为 3+4+5 = 12 。
数据规模与约定
对于20%的数据, n <= 20。
对于50%的数据, n <= 1000。
对于100%的数据, n <= 100000。
权值均为不超过1000的正整数。
分析💬
树的存储结构原本可以用“孩子表示法”,可是此题的输入并不是先父结点后子结点,因此,我们不妨用无向图来看待此树(树其实也是图)。而此题结点数比较大,用“数组表示法”显然不合适(内存可能不够),“十字链表”和“邻接多重表”则比较繁琐,“邻接表”作为存储结构最为合适。
采用先序遍历的算法遍历整棵树。
当前子树的根结点可以选择,可以不选择。
如果根结点不选择,那么它的某个子结点可不选择,也可选择。只需选取其中权值较大的那种加到根结点的权值中即可。
如果根结点选择,那么它的所有子结点都不能选择。也就是将子结点不选择的那种的权值加到根结点权值中即可。
不选择当前子树的根结点,那么就将它的子结点(子树)i
的权值较大的情况加到根结点中
weight[root][0] += Math.max(weight[i][0], weight[i][1]);
选择当前子树的根结点,那么将它的子结点(子树)i
的不选择的权值加到根结点中
weight[root][1] += weight[i][0];
代码💻
/**
* Time 3505ms
* @author wowpH
* @version 1.1
* @date 2019年6月7日上午10:57:35
* Environment: Windows 10
* IDE Version: Eclipse 2019-3
* JDK Version: JDK1.8.0_112
*/
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
private Scanner sc;
private static final int MAX_N = 100001;
private int n;
private int[][] weight;// 权值
private List<List<Integer>> adjacencyList;// 邻接表
public Main() {
weight = new int[MAX_N][2];// 下标从1开始
sc = new Scanner(new InputStreamReader(System.in));
while (sc.hasNext()) {
adjacencyList = 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() {
n = sc.nextInt();// 结点数
adjacencyList.add(new ArrayList<Integer>());// 下标从1开始,这个不用
for (int i = 1; i <= n; i++) {
weight[i][0] = 0; // 初始化为0
weight[i][1] = sc.nextInt();// 输入权值
adjacencyList.add(new ArrayList<Integer>());// 创建头结点
}
int head, tail;// 弧的头尾
for (int i = 1; i < n; i++) {
tail = sc.nextInt();
head = sc.nextInt();
adjacencyList.get(tail).add(head);// 添加表结点
adjacencyList.get(head).add(tail);// 无向图,添加表结点
}
}
private void dfs(int root, int pre) {// root根,pre前驱结点
List<Integer> list = adjacencyList.get(root);// 当前链表
for (Integer i : list) {
if (i != pre) {// 非叶子结点,继续向下递归
dfs(i, root);
// 不选root点,选root子结点i最大的情况
weight[root][0] += Math.max(weight[i][0], weight[i][1]);
// 选root点,不选root子结点i的情况
weight[root][1] += weight[i][0];
}
}
}
public static void main(String[] args) {
new Main();
}
}