当前位置:   article > 正文

蓝桥杯结点选择(树形动态规划)_蓝桥杯树上选点

蓝桥杯树上选点

这道题就是一道树的最大独立集问题。先看一下题目要求:

问题描述
有一棵 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的正整数。


好的那么我们看一下解决思路:

首先一定是要接收输入的值,

再者就是把结点相连,构建成树。

题目告诉不一定是二叉树,所以可以看做图来处理

可以使用邻接表存储建立一个数组tree[i][j]表示结点i的第j个孩子结点

dp[i][0]表示不取i结点的结果

dp[i][1]表示取i结点的结果

用深度优先搜索和动态规划,每个点的最大权值有两种情况,取和不取该结点,如果取了这个结点,哪么与它相邻的点就不能取了;不取当前点则取与他相邻点的最大值进行累加,从底向上累加到顶部:max(dp[i][1],dp[i][0])就是要求的结果。

用一个变量root保存当前结点的前一个结点,如果等于了root说明访问到了它的父亲结点,为了防止重复访问,要在tree[start][i]不等于root时候继续dfs下去,否则,可能成为无限循环的环。

思路就是这样,让我们一起撸代码吧!

  1. import java.util.Scanner;
  2. public class Main {
  3. public int[][] dp = new int[100002][2];
  4. public int[][] tree = new int[100002][300]; //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
  5. /*
  6. * 参数point1:表示输入的第point1个节点,不是节点权值
  7. * 参数point2:表示输入的第point2的节点,不是节点权值
  8. * 采用邻接表来存储数据的值。
  9. */
  10. public void creatTree(int point1, int point2) {
  11. int i = 0;
  12. //当第point1个节点为父母节点时
  13. while(tree[point1][i] != 0) i++; //如果第point1个节点已经有孩子了,再增加一个孩子
  14. tree[point1][i] = point2;
  15. int j = 0;
  16. //当第point2个节点为父母节点时
  17. while(tree[point2][j] != 0) j++;
  18. tree[point2][j] = point1;
  19. }
  20. /*
  21. * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
  22. * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
  23. */
  24. public void dfs(int start, int root) {
  25. int child = tree[start][0]; //第start个节点的第1个孩子节点
  26. for(int i = 0;child != 0;i++) {
  27. child = tree[start][i];
  28. if(child != root) { //防止出现start的孩子成为start的父亲情况
  29. dfs(child, start);
  30. dp[start][1] += dp[child][0];
  31. dp[start][0] += (dp[child][1] > dp[child][0] ? dp[child][1] : dp[child][0]);
  32. }
  33. }
  34. }
  35. public static void main(String[] args) {
  36. Main test = new Main();
  37. Scanner in = new Scanner(System.in);
  38. int n = in.nextInt();
  39. for(int i = 0;i < n;i++)
  40. test.dp[i + 1][1] = in.nextInt();
  41. for(int i = 0;i < n - 1;i++) {
  42. int point1 = in.nextInt();
  43. int point2 = in.nextInt();
  44. test.creatTree(point1, point2);
  45. }
  46. test.dfs(1, 0); //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
  47. int max = (test.dp[1][1] > test.dp[1][0] ? test.dp[1][1] : test.dp[1][0]);
  48. System.out.println(max);
  49. }
  50. }

从运行结果来看,这道题是正确的。

但是提交之后,是超时的只得了50分,所以,还需要改进。

搜索了一下超时的原因:说是Scanner效率太低,所以使用了BufferedReader+StringTokenizer的方法依然超时。

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStreamReader;
  4. import java.util.Scanner;
  5. import java.util.StringTokenizer;
  6. public class Main {
  7. public int[][] dp = new int[100002][2];
  8. public int[][] tree = new int[100002][300]; //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
  9. /*
  10. * 参数point1:表示输入的第point1个节点,不是节点权值
  11. * 参数point2:表示输入的第point2的节点,不是节点权值
  12. * 说明:由于题目仅仅给出边的说明,并未说明两个节点谁是父母节点,所以以下有两种情形
  13. */
  14. public void creatTree(int point1, int point2) {
  15. int i = 0;
  16. //当第point1个节点为父母节点时
  17. while(tree[point1][i] != 0) i++; //如果第point1个节点已经有孩子了,再增加一个孩子
  18. tree[point1][i] = point2;
  19. int j = 0;
  20. //当第point2个节点为父母节点时
  21. while(tree[point2][j] != 0) j++;
  22. tree[point2][j] = point1;
  23. }
  24. /*
  25. * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
  26. * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
  27. */
  28. public void dfs(int start, int root) {
  29. int child = tree[start][0]; //第start个节点的第1个孩子节点
  30. for(int i = 0;child != 0;i++) {
  31. child = tree[start][i];
  32. if(child != root) { //防止出现start的孩子成为start的父亲情况
  33. dfs(child, start);
  34. dp[start][1] += dp[child][0];
  35. dp[start][0] += (dp[child][1] > dp[child][0] ? dp[child][1] : dp[child][0]);
  36. }
  37. }
  38. }
  39. public static void main(String[] args) throws IOException {
  40. Main test = new Main();
  41. //Scanner in = new Scanner(System.in);
  42. BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
  43. String input=reader.readLine();
  44. int n = Integer.parseInt(input);
  45. String input1=reader.readLine();
  46. StringTokenizer tok0=new StringTokenizer(input1);
  47. for(int i = 0;i < n;i++) {
  48. test.dp[i + 1][1] = Integer.valueOf(tok0.nextToken());
  49. }
  50. for(int i = 0;i < n - 1;i++) {
  51. String input2=reader.readLine();
  52. StringTokenizer tok=new StringTokenizer(input2);
  53. int point1=Integer.valueOf(tok.nextToken());
  54. int point2=Integer.valueOf(tok.nextToken());
  55. test.creatTree(point1, point2);
  56. }
  57. test.dfs(1, 0); //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
  58. int max = (test.dp[1][1] > test.dp[1][0] ? test.dp[1][1] : test.dp[1][0]);
  59. System.out.println(max);
  60. }
  61. }
在网上找到一个AC了的代码,分析一下代码的缺陷在哪,
  1. import java.io.*;
  2. import java.util.*;
  3. public class Main {
  4. final static int MAX_N = 100010;
  5. PrintWriter out;
  6. InputReader cin;
  7. int edgecnt;
  8. int dp[][] = new int[MAX_N][2];
  9. Edge E[] = new Edge[MAX_N * 2];
  10. int head[] = new int[MAX_N];
  11. int sta[] = new int[MAX_N * 2];
  12. boolean vis[] = new boolean[MAX_N];
  13. class Edge {
  14. int u, v, nxt;
  15. Edge () {
  16. }
  17. Edge (int _u, int _v, int _n) {
  18. u = _u;
  19. v = _v;
  20. nxt = _n;
  21. }
  22. }
  23. class InputReader {
  24. InputReader(InputStream in) {
  25. reader = new BufferedReader(new InputStreamReader(in));
  26. tokenizer = new StringTokenizer("");
  27. }
  28. private String next() throws IOException {
  29. while (!tokenizer.hasMoreTokens()) {
  30. tokenizer = new StringTokenizer(reader.readLine());
  31. }
  32. return tokenizer.nextToken();
  33. }
  34. public Integer nextInt() throws IOException {
  35. return Integer.parseInt(next());
  36. }
  37. private BufferedReader reader;
  38. private StringTokenizer tokenizer;
  39. }
  40. void add(int u, int v) {
  41. E[edgecnt] = new Edge(u, v, head[u]);
  42. head[u] = edgecnt++;
  43. }
  44. void dfs(int x, int fa) {
  45. Arrays.fill(vis, false);
  46. int top = 0;
  47. vis[x] = true;
  48. sta[top++] = x;
  49. while (top > 0) {
  50. int u = sta[top - 1];
  51. boolean Ed = false;
  52. for (int i = head[u]; i + 1 != 0; i = E[i].nxt) {
  53. int v = E[i].v;
  54. if (vis[v]) continue;
  55. Ed = true;
  56. sta[top++] = v;
  57. vis[v] = true;
  58. }
  59. if (Ed) continue;
  60. --top;
  61. for (int i = head[u]; i + 1 != 0; i = E[i].nxt) {
  62. int v = E[i].v;
  63. dp[v][0] += Math.max(dp[u][0], dp[u][1]);
  64. dp[v][1] += dp[u][0];
  65. }
  66. }
  67. }
  68. void run() throws IOException {
  69. int n = cin.nextInt();
  70. for (int i = 1; i <= n; ++i)
  71. dp[i][1] = cin.nextInt();
  72. Arrays.fill(head, -1);
  73. for (int i = 1; i < n; ++i) {
  74. int u = cin.nextInt();
  75. int v = cin.nextInt();
  76. add(u, v);
  77. add(v, u);
  78. }
  79. dfs(1, -1);
  80. int ans = Math.max(dp[1][0], dp[1][1]);
  81. out.println(ans);
  82. out.close();
  83. }
  84. public static void main(String[] args) throws IOException {
  85. new Main().run();
  86. }
  87. Main() {
  88. cin = new InputReader(System.in);
  89. out = new PrintWriter(System.out);
  90. }
  91. }
这是别人写的代码让我们看一下问题。

将代码改写成下面这样依然不行,说明在思路上除了问题

  1. import java.io.BufferedReader;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.InputStreamReader;
  5. import java.io.PrintWriter;
  6. import java.util.StringTokenizer;
  7. public class Main {
  8. public int[][] dp = new int[100002][2];
  9. public int[][] tree = new int[100002][300]; //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
  10. PrintWriter out;
  11. InputReader cin;
  12. /*
  13. * 参数point1:表示输入的第point1个节点,不是节点权值
  14. * 参数point2:表示输入的第point2的节点,不是节点权值
  15. * 说明:由于题目仅仅给出边的说明,并未说明两个节点谁是父母节点,所以以下有两种情形
  16. */
  17. public void creatTree(int point1, int point2) {
  18. int i = 0;
  19. //当第point1个节点为父母节点时
  20. while(tree[point1][i] != 0) i++; //如果第point1个节点已经有孩子了,再增加一个孩子
  21. tree[point1][i] = point2;
  22. int j = 0;
  23. //当第point2个节点为父母节点时
  24. while(tree[point2][j] != 0) j++;
  25. tree[point2][j] = point1;
  26. }
  27. /*
  28. * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
  29. * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
  30. */
  31. public void dfs(int start, int root) {
  32. int child = tree[start][0]; //第start个节点的第1个孩子节点
  33. for(int i = 0;child != 0;i++) {
  34. child = tree[start][i];
  35. if(child != root) { //防止出现start的孩子成为start的父亲情况
  36. dfs(child, start);
  37. dp[start][1] += dp[child][0];
  38. dp[start][0] += Math.max(dp[child][1], dp[child][0]);
  39. }
  40. }
  41. }
  42. void run() throws IOException {
  43. int n = cin.nextInt();
  44. for(int i = 0;i < n;i++) {
  45. dp[i + 1][1] = cin.nextInt();
  46. }
  47. for(int i = 0;i < n - 1;i++) {
  48. int point1=cin.nextInt();
  49. int point2=cin.nextInt();
  50. creatTree(point1, point2);
  51. }
  52. dfs(1, 0); //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
  53. int max = Math.max(dp[1][1], dp[1][0]);
  54. out.println(max);
  55. out.close();
  56. }
  57. public static void main(String[] args) throws IOException {
  58. new Main().run();
  59. }
  60. Main() {
  61. cin = new InputReader(System.in);
  62. out = new PrintWriter(System.out);
  63. }
  64. class InputReader {
  65. InputReader(InputStream in) {
  66. reader = new BufferedReader(new InputStreamReader(in));
  67. tokenizer = new StringTokenizer("");
  68. }
  69. private String next() throws IOException {
  70. while (!tokenizer.hasMoreTokens()) {
  71. tokenizer = new StringTokenizer(reader.readLine());
  72. }
  73. return tokenizer.nextToken();
  74. }
  75. public Integer nextInt() throws IOException {
  76. return Integer.parseInt(next());
  77. }
  78. private BufferedReader reader;
  79. private StringTokenizer tokenizer;
  80. }
  81. }

路过的大神,帮帮忙!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/64240
推荐阅读
相关标签
  

闽ICP备14008679号