资源限制
时间限制: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的正整数。
Code
Java源代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.StringTokenizer;
/*
* dfs函数还是存在不明白地方
*
* */
public class Main {
// 全局变量
final static int MAX = 100010;
int edgecount;
int tree[] = new int[MAX];
int dp[][] = new int[MAX][2];
int visit[] = new int[MAX * 2]; // visit数组表示访问的顺序?????
boolean visited[] = new boolean[MAX];
// 树的边类
class Edge {
int start, end, next; // start为end的父亲节点,end为start的子节点,next为start的下一个有边相连的节点
// 边的Edge实体类
Edge(int s, int e, int n) {
start = s;
end = e;
next = n;
}
}
Edge edge[] = new Edge[MAX * 2]; // edge数组中一个元素包含三个属性:start,end,next;边的数组大小为2倍的MAX,即父节点到子节点的边被存两次(父节点到子节点存一次,子节点到父节点也存一次)
void add(int start, int end) { // 往树上添加边
edge[edgecount] = new Edge(start, end, tree[start]);
tree[start] = edgecount++; // tree[start]的值:边以start节点为初始节点的边的个数的2倍
}
void dfs(int x, int x_father) { // x与x_father(x的父节点)
Arrays.fill(visited, false); // 把visited数组(该节点是否已经访问过)内的元素值全部置为false,证明刚开始都未访问过
int temp = 0; //
visited[x] = true; // 访问x节点
visit[temp++] = x; // visit[0]=x
while (temp > 0) {
x = visit[temp - 1];
boolean edgevisited = false; // edgevisited为边是否被问过,false表示未被访问过
// for循环结束条件为:
for (int i = tree[x]; i + 1 != 0; i = edge[i].next) { // next--与start有边相连的下一个节点
int end = edge[i].end; // 找父节点的第一个子节点
if (visited[end]) // 如果父节点的第一个子节点被访问过了,则执行continue跳出本次循环
continue; // 执行了continue证明退出本次for循环;下一次for循环i被赋为了start的第二个有边相连的节点...
// 如果父节点的字节点未被访问,则执行下边三个语句
edgevisited = true; // 边的访问状态为true(被访问了)
visit[temp++] = end;
visited[end] = true; // 因为父节点的第一个字节点未被访问才执行这三条语句,所以要把父节点的第一个子节点的访问状态置为true
// 三条语句都执行结束后也进行下一次循环,i被赋为了start的第二个有边相连的节点...
}
if (edgevisited)
continue;
--temp;
for (int i = tree[x]; i + 1 != 0; i = edge[i].next) {
int x_son = edge[i].end; // x的子节点
dp[x_son][0] += Math.max(dp[x][0], dp[x][1]);
dp[x_son][1] += dp[x][0];
}
}
}
void run() throws IOException {
int n = cin.nextInt(); // n代表树有n个节点
for (int i = 1; i <= n; ++i) {
dp[i][1] = cin.nextInt(); // dp[i][1]的值为树中i号节点代表的节点值
}
Arrays.fill(tree, -1); // 将tree数组中所有值都置为-1
for (int i = 1; i < n; ++i) { // n-1 行代表树中的n-1条边,每行描述树上的一条边.
int x = cin.nextInt();
int y = cin.nextInt();
add(x, y); // x为start,y为end,存入edge数组中
add(y, x); // y为start,x为end,存入edge数组中
}
dfs(1, 0); // 0号节点为最初始的祖先节点,1号节点为0号节点的子节点,开始进行dfs
int ans = Math.max(dp[1][0], dp[1][1]);
out.println(ans); // ans即为最终结果
out.close();
}
public static void main(String[] args) throws IOException {
new Main().run();
}
Main() {
cin = new InputReader(System.in);
out = new PrintWriter(System.out);
}
InputReader cin;
PrintWriter out;
class InputReader {
InputReader(InputStream in) {
reader = new BufferedReader(new InputStreamReader(in));
tokenizer = new StringTokenizer(" ");
}
private String next() throws IOException {
while (!tokenizer.hasMoreTokens()) {
tokenizer = new StringTokenizer(reader.readLine());
}
return tokenizer.nextToken();
}
public Integer nextInt() throws IOException {
return Integer.parseInt(next());
}
private BufferedReader reader;
private StringTokenizer tokenizer;
}
}