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

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

问题描述
有一棵 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下去,否则,可能成为无限循环的环。

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

import java.util.Scanner;

public class Main {
    public int[][] dp = new int[100002][2];   
    public int[][] tree = new int[100002][300];  //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
    /*
     * 参数point1:表示输入的第point1个节点,不是节点权值
     * 参数point2:表示输入的第point2的节点,不是节点权值
     * 采用邻接表来存储数据的值。
     */
    public void creatTree(int point1, int point2) {
        int i = 0;
        //当第point1个节点为父母节点时
        while(tree[point1][i] != 0) i++;  //如果第point1个节点已经有孩子了,再增加一个孩子  
        tree[point1][i] = point2;
        int j = 0;
        //当第point2个节点为父母节点时
        while(tree[point2][j] != 0) j++;
        tree[point2][j] = point1;
    }
    /*
     * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
     * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
     */
    public void dfs(int start, int root) {
        int child = tree[start][0];  //第start个节点的第1个孩子节点
        for(int i = 0;child != 0;i++) {
            child = tree[start][i];
            if(child != root) {  //防止出现start的孩子成为start的父亲情况
                dfs(child, start);
                dp[start][1] += dp[child][0]; 
                dp[start][0] += (dp[child][1] > dp[child][0] ? dp[child][1] : dp[child][0]);
            }
        }
    }
    
    public static void main(String[] args) {
        Main test = new Main();
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        for(int i = 0;i < n;i++)
            test.dp[i + 1][1] = in.nextInt();
        for(int i = 0;i < n - 1;i++) {
            int point1 = in.nextInt();
            int point2 = in.nextInt();
            test.creatTree(point1, point2);
        }
        test.dfs(1, 0);   //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
        int max = (test.dp[1][1] > test.dp[1][0] ? test.dp[1][1] : test.dp[1][0]);
        System.out.println(max);
    }
}

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

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

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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
import java.util.StringTokenizer;

public class Main {
    public int[][] dp = new int[100002][2];   
    public int[][] tree = new int[100002][300];  //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
    /*
     * 参数point1:表示输入的第point1个节点,不是节点权值
     * 参数point2:表示输入的第point2的节点,不是节点权值
     * 说明:由于题目仅仅给出边的说明,并未说明两个节点谁是父母节点,所以以下有两种情形
     */
    public void creatTree(int point1, int point2) {
        int i = 0;
        //当第point1个节点为父母节点时
        while(tree[point1][i] != 0) i++;  //如果第point1个节点已经有孩子了,再增加一个孩子  
        tree[point1][i] = point2;
        int j = 0;
        //当第point2个节点为父母节点时
        while(tree[point2][j] != 0) j++;
        tree[point2][j] = point1;
    }
    /*
     * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
     * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
     */
    public void dfs(int start, int root) {
        int child = tree[start][0];  //第start个节点的第1个孩子节点
        for(int i = 0;child != 0;i++) {
            child = tree[start][i];
            if(child != root) {  //防止出现start的孩子成为start的父亲情况
                dfs(child, start);
                dp[start][1] += dp[child][0];  
                dp[start][0] += (dp[child][1] > dp[child][0] ? dp[child][1] : dp[child][0]);
            }
        }
    }
    
    public static void main(String[] args) throws IOException {
        Main test = new Main();
        //Scanner in = new Scanner(System.in);
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        
        String input=reader.readLine();
        int n =	Integer.parseInt(input);
        String input1=reader.readLine();
        StringTokenizer tok0=new StringTokenizer(input1);
        for(int i = 0;i < n;i++) {
            test.dp[i + 1][1] = Integer.valueOf(tok0.nextToken());
        }
        for(int i = 0;i < n - 1;i++) {
        	String input2=reader.readLine();
        	StringTokenizer tok=new StringTokenizer(input2);
            int point1=Integer.valueOf(tok.nextToken());
            int point2=Integer.valueOf(tok.nextToken());
            test.creatTree(point1, point2);
        }
        test.dfs(1, 0);   //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
        int max = (test.dp[1][1] > test.dp[1][0] ? test.dp[1][1] : test.dp[1][0]);
        System.out.println(max);
    }
}
在网上找到一个AC了的代码,分析一下代码的缺陷在哪,
import java.io.*;
import java.util.*;

public class Main {
	
	final static int MAX_N = 100010;

	PrintWriter out;
	InputReader cin;
	int edgecnt;
	int dp[][] = new int[MAX_N][2];
	Edge E[] = new Edge[MAX_N * 2];
	int head[] = new int[MAX_N];
	int sta[] = new int[MAX_N * 2];
	boolean vis[] = new boolean[MAX_N];
	
	class Edge {
		int u, v, nxt;
		Edge () {
			
		}
		Edge (int _u, int _v, int _n) {
			u = _u;
			v = _v;
			nxt = _n;
		}
	}
	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;
	}

	
	void add(int u, int v) {
		E[edgecnt] = new Edge(u, v, head[u]);
		head[u] = edgecnt++;
	}
	
	void dfs(int x, int fa) {
		Arrays.fill(vis, false);
		int top = 0;
		vis[x] = true;
		sta[top++] = x;
		while (top > 0) {
			int u = sta[top - 1];
			boolean Ed = false;
			for (int i = head[u]; i + 1 != 0; i = E[i].nxt) {
				int v = E[i].v;
				if (vis[v]) continue;
				Ed = true;
				sta[top++] = v;
				vis[v] = true;
			}
			if (Ed) continue;
			--top;
			for (int i = head[u]; i + 1 != 0; i = E[i].nxt) {
				int v = E[i].v;
				dp[v][0] += Math.max(dp[u][0], dp[u][1]);
				dp[v][1] += dp[u][0];
				
			}
		}
	}
	
	void run() throws IOException {
		int n = cin.nextInt();
		
		for (int i = 1; i <= n; ++i) 
			dp[i][1] = cin.nextInt();
		Arrays.fill(head, -1);
		for (int i = 1; i < n; ++i) {
			int u = cin.nextInt();
			int v = cin.nextInt();
			add(u, v);
			add(v, u);
		}
		dfs(1, -1);
		int ans = Math.max(dp[1][0], dp[1][1]);
		out.println(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);
	}

}
这是别人写的代码让我们看一下问题。

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

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.StringTokenizer;

public class Main {
    public  int[][] dp = new int[100002][2];   
    public int[][] tree = new int[100002][300];  //tree[i][3] = num表示第i个节点的第3个孩子节点为第num个节点
    PrintWriter out;
    InputReader cin;
    /*
     * 参数point1:表示输入的第point1个节点,不是节点权值
     * 参数point2:表示输入的第point2的节点,不是节点权值
     * 说明:由于题目仅仅给出边的说明,并未说明两个节点谁是父母节点,所以以下有两种情形
     */
    public void creatTree(int point1, int point2) {
        int i = 0;
        //当第point1个节点为父母节点时
        while(tree[point1][i] != 0) i++;  //如果第point1个节点已经有孩子了,再增加一个孩子  
        tree[point1][i] = point2;
        int j = 0;
        //当第point2个节点为父母节点时
        while(tree[point2][j] != 0) j++;
        tree[point2][j] = point1;
    }
    /*
     * 参数satrt:开始对树进行DFS遍历的开始节点,为具体节点位置,不是节点权值
     * 参数root:为第start个节点的直接父母节点位置,root = 0表示根节点的父母节点
     */
    public void dfs(int start, int root) {
        int child = tree[start][0];  //第start个节点的第1个孩子节点
        for(int i = 0;child != 0;i++) {
            child = tree[start][i];
            if(child != root) {  //防止出现start的孩子成为start的父亲情况
                dfs(child, start);
                dp[start][1] += dp[child][0];  
                dp[start][0] += Math.max(dp[child][1], dp[child][0]);
            }
        }
    }
    void run() throws IOException {
    	 int n =	cin.nextInt();
         for(int i = 0;i < n;i++) {
             dp[i + 1][1] = cin.nextInt();
         }
         for(int i = 0;i < n - 1;i++) {
             int point1=cin.nextInt();
             int point2=cin.nextInt();
             creatTree(point1, point2);
         }
         dfs(1, 0);   //从创建的数的根节点(即第1个顶点,0表示根节点的父母节点)开始进行DFS遍历
         int max = Math.max(dp[1][1], dp[1][0]);
         out.println(max);
         out.close();
	}
    public static void main(String[] args) throws IOException {
    	new Main().run();
    }
    
    Main() {
		 cin = new InputReader(System.in);
		 out = new PrintWriter(System.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;
	}
}

路过的大神,帮帮忙!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值