* * * ALGO-4 结点选择

资源限制

时间限制: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;
	}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值