算法训练 结点选择
@蓝桥杯 算法训练 ALGO-4
问题描述
有一棵 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.io.*;
import java.util.ArrayList;
import java.util.StringTokenizer;
public class Main {
public static void main(String[] args) throws IOException {
InputReader read = new InputReader(System.in, " ");
int n = read.nextInt();
int [][] dp = new int[2][n + 1];
ArrayList<Integer>[] graph = new ArrayList[n + 1];
for (int i = 1; i <= n; i++) {
dp[1][i] = read.nextInt();
graph[i] = new ArrayList();
}
for (int i = 1, V, E; i < n; i++) {
V = read.nextInt(); E = read.nextInt();
graph[V].add(E);
graph[E].add(V);
}
read.close();
System.out.print(toWeight(graph, dp, dfs(graph, n, 1)));
}
static int[] dfs(ArrayList<Integer>[] graph, int size, int E) {
boolean[] marked = new boolean[size + 1];
int[] path = new int[size--];
path[size] = E;
marked[E] = true;
for (int l = size; l >=0; l--)
for (int i :graph[path[l]])
if (!marked[i]) marked[(path[--size] = i)] = true;
return path;
}
static int toWeight(ArrayList<Integer>[] graph, int[][] dp, int[] path) {
for (int E: path)
for (int V: graph[E]) {
dp[1][V] += dp[0][E];
dp[0][V] += Math.max(dp[0][E], dp[1][E]);
}
return Math.max(dp[0][1], dp[1][1]);
}
static class InputReader {
BufferedReader read;
StringTokenizer tokens;
String delimiters;
InputReader (InputStream in, String delimiters) {
this.read = new BufferedReader(new InputStreamReader(in));
this.delimiters = delimiters;
this.tokens = new StringTokenizer("", delimiters);
}
InputReader (InputStream in) { this(in, " \t\n\r\f"); }
String next() throws IOException {
while (!tokens.hasMoreTokens()) tokens = new StringTokenizer(read.readLine(), delimiters);
return tokens.nextToken();
}
int nextInt() throws IOException { return Integer.parseInt(next()); }
void close() throws IOException { read.close(); }
}
}
下面是改进前的历程
邻接表
用邻接表来描述每个点的所有边,用二维数组存储邻接表。
没有特殊的说明,所以即使题目指明是棵树,但这也是个图问题。树也是图
回溯开始动态规划,计算子图顶点选出与否往下能带来的最大权值。
没有能力讲的很深,见谅
最后只跑了50分,因为超时。
源码:
import java.util.Scanner;
public class Main {
static int n;
static int[][] dp;
static int[][] graph;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dp = new int[n + 1][2];
graph = new int[n + 1][4];
for (int i = 1; i <= n; i++)
dp[i][1] = sc.nextInt();
for (int i = 1; i < n; i++) {
int V = sc.nextInt(), E = sc.nextInt();
if (++graph[V][0] == graph[V].length) graph[V] = reSize(graph[V], graph[V].length);
graph[V][graph[V][0]] = E;
if (++graph[E][0] == graph[E].length) graph[E] = reSize(graph[E], graph[E].length);
graph[E][graph[E][0]] = V;
}
Lost(1, 0);
System.out.println(Math.max(dp[1][0], dp[1][1]));
}
static int[] reSize(int[] former,int size) {
int[] temp = new int[size * 2];
System.arraycopy(former, 0, temp, 0, size);
return temp;
}
static void Lost(int E, int V) {
for (int i = 1; i <= graph[E][0]; i++) {
int temp = graph[E][i];
if (temp != V) {
Lost(temp, E);
dp[E][1] += dp[temp][0];
dp[E][0] += Math.max(dp[temp][0], dp[temp][1]);
}
}
}
}
不过一段时间过后它就能跑70分了
因为蓝桥系统给出的数据终于像个人了
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
static int [][] dp;
static ArrayList<Integer>[] graph;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
dp = new int[2][n + 1];
graph = new ArrayList[n + 1];
for (int i = 1; i <= n; i++) {
dp[1][i] = sc.nextInt();
graph[i] = new ArrayList();
}
for (int i = 1, V, E; i < n; i++) {
V = sc.nextInt(); E = sc.nextInt();
graph[V].add(E);
graph[E].add(V);
}
dfs(1, 0);
System.out.print(Math.max(dp[0][1], dp[1][1]));
}
static void dfs(int E, int V) {
for (int i: graph[E])
if (i != V) {
dfs(i, E);
dp[1][E] += dp[0][i];
dp[0][E] += Math.max(dp[0][i], dp[1][i]);
}
}
}
突然有一天在简单了解 BufferedReader 和 Scanner 直接的区别后
封装了一个 BufferedReader 进行对比,需要 nextDouble 之类的方法可以自行封装
static class InputReader {
BufferedReader read;
StringTokenizer tokens;
String delimiters;
InputReader (InputStream in, String delimiters) {
this.read = new BufferedReader(new InputStreamReader(in));
this.delimiters = delimiters;
this.tokens = new StringTokenizer("", delimiters);
}
InputReader (InputStream in) { this(in, " \t\n\r\f"); }
String next() throws IOException {
while (!tokens.hasMoreTokens()) tokens = new StringTokenizer(read.readLine(), delimiters);
return tokens.nextToken();
}
int nextInt() throws IOException { return Integer.parseInt(next()); }
void close() throws IOException { read.close(); }
}
使用方法:
InputReader read = new InputReader(System.in, " ");
read.nextInt();
可以看到虽然性能有显著提升,但还是未能满足资源限制,并且出现了运行错误的情况
使用测试用例
InputReader read = new InputReader(new FileInputStream("C:\\Users\\admin\\Downloads\\input8.txt"), " ");
出现了 java.lang.StackOverflowError 栈溢出 错误
递归计划暴死,等会研究出能跑满的方法再发出来
It’s time to let go?