算法:节点选择--动态规划时间超时问题(未解决)

节点选择

问题描述
有一棵 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.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.StringTokenizer;
import java.util.Arrays;

public class Main {
        public static void main(String[]args) throws Exception{
                //测试运行时间
                //long Start = System.nanoTime();

                //读取输入并构造整颗树
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                StringTokenizer st = new StringTokenizer(br.readLine());
                int n = Integer.parseInt(st.nextToken());
                Node[] arr = new Node[n + 1];
                st = new StringTokenizer(br.readLine());
                for(int i = 1;i <= n;i++){
                        arr[i] = new Node();
                        arr[i].setNow(i);
                        arr[i].setData(Integer.parseInt(st.nextToken()));
                }
                ArrayList<Integer>[] aList = new ArrayList[n + 1];
                for(int i = 1; i <= n;i++){
                        aList[i] = new ArrayList<Integer>();
                }
                int[] list = new int[n + 1];
                list[0] = 1;
                Queue<Integer[]> s = new LinkedList<Integer[]>();
                int end = 0;
                for(int i = 1; i < n;i++){
                        st = new StringTokenizer(br.readLine());
                        int start = Integer.parseInt(st.nextToken());
                        int next = Integer.parseInt(st.nextToken());
                        if(list[next] == 1){
                                list[start] = 1;
                                aList[next].add(start);
                        }
                        else if(list[start] == 1){
                                list[next] = 1;
                                aList[start].add(next);
                        }
                        else if(list[start] == 2){
                                list[next] = 1;
                                aList[start].add(next);
                        }
                        else if(list[next] == 2){
                                list[start] = 1;
                                aList[next].add(start);
                        }
                        else{
                                if(i != 1){
                                        Integer[] list1 = new Integer[2];
                                        list1[0] = start;
                                        list1[1] = next;
                                        s.add(list1);
                                }
                                else{
                                        end = start;
                                        list[start] = 2;
                                        list[next] = 1;
                                        aList[start].add(next);
                                }
                        }
                }
                br.close();
                while(s.size() != 0){
                        Integer[] list1 = s.peek();
                        int start = list1[0];
                        int next = list1[1];
                        if(list[next] == 1){
                                list[start] = 1;
                                aList[next].add(start);
                        }
                        else if(list[start] == 1){
                                list[next] = 1;
                                aList[start].add(next);
                        }
                        else{
                                s.add(list1);
                        }
                        s.poll();
                }
                for(int i = 1;i <= n;i++){
                        arr[i].setNext(aList[i].stream().mapToInt(k -> k).toArray());
                }
                //填入各个结点所处树的高度
                arr[end].setHeight(1);
                int h = 2;
                ArrayList<Integer> alist = new ArrayList<Integer>();
                alist.add(end);
                while(alist.size() != 0){
                        ArrayList<Integer> alistTemp = new ArrayList<Integer>();
                        for(Integer flag:alist){
                                for(int i:arr[flag].getNext()){
                                        arr[i].setHeight(h);
                                        alistTemp.add(i);
                                }
                        }
                        h++;
                        alist = alistTemp;
                }

                //打印结点信息
                //for(int i = 1;i < arr.length;i++){
                //    System.out.println(arr[i].toString());
                //}

                //此处h在上述循环中在最后的两次循环多家两次,在此减取,此时h为树的高度
                h = h - 2;
                //max[]以各个结点为根结点的树的最大权值
                int[]max = new int[n + 1];
                for(int a = h;a >= 1;a--){
                        for(int i = 1;i <= n;i++){
                                //从树底向上计算以各个结点为根节点的树的最大权值
                                if(arr[i].getHeight() == a){
                                        if(a == h){
                                                max[i] = arr[i].getData();
                                        }
                                        else if(a == h - 1){
                                                for(int j:arr[i].getNext()){
                                                        max[i] += max[j];
                                                }
                                                max[i] = max[i] < arr[i].getData()?arr[i].getData():max[i];
                                        }
                                        else{
                                                int max2 = 0;
                                                int max3 = 0;
                                                for(int j:arr[i].getNext()){
                                                        max2 += max[j];
                                                        for(int k:arr[j].getNext()){
                                                                max3 += max[k];
                                                        }
                                                }
                                                max[i] = max2 < arr[i].getData() + max3?arr[i].getData() + max3:max2;
                                        }
                                        if(a == 1){
                                                //打印树的根结点的最大权值,即为最大权值
                                                System.out.println(max[i]);
                                        }
                                }
                        }
                }

                //打印测试时间
                //long End = System.nanoTime();
                //System.out.println((End - Start)/Math.pow(10,9));
        }

}

class Node{
        private int now;
        private int[] next;
        private int data;
        private int height = Integer.MAX_VALUE;

        public void setNow(int now){
                this.now = now;
        }

        public int getNow(){
                return now;
        }

        public void setNext(int[] next){
                this.next = next;
        }

        public int[] getNext(){
                return next;
        }

        public void setData(int data){
                this.data = data;
        }

        public int getData(){
                return data;
        }

        public void setHeight(int height){
                this.height = height;
        }

        public int getHeight(){
                return height;
        }

        @Override
        public String toString(){
                return "{now:" + now + ",next:" + Arrays.toString(next) + ",data:" + data + ",height:" + height + "}";
        }

}

思路
示例图
算最大权值即计算根结点的权值。而计算根结点的权值与以下一层的结点为根结点的树的最大权值和以第三层的结点为根结点的树的最大权值有关。
以上图为例,计算最大权值即计算以1为根节点的最大权值,此时有两种情况:

  1. 选择1号结点,此时第二层的结点都无法选择,只能从第三层结点开始计算以第三层结点为根结点的树的最大权值,此时最大权值即为根结点1号的权值加上以第三层(4,5,6)为根结点的树的最大权值所有之和。
  2. 不选择1号结点,此时第二层结点可选,此时最大权值即为以第二层(2,3)为根结点的树的最大权值所有之和。

由上诉思路可以看出可以使用动态规划,由树底开始计算以各个结点为根结点的最大权值,并向上计算,直到算出出根结点的最大权值即为最大权值。

时间超时问题
猜测:上述算法默认选择第一条边的第一个结点为根结点构造树,此时可能出现树的不平衡,可能会导致时间超时,并未测试,尚未解决。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值