无根树转有根树

https://blog.csdn.net/qq_32823673/article/details/56838405
刚开始代码,这个代码有点问题,不知道为什么一直报空指针异常

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

class Test{
    static int MAx =  1000001;
    static int p[] = new int[MAx];
    static List<Integer>[]g ;
    static int n;
    static Scanner sc = new Scanner(System.in);
    public static void main(String[] args) {
        g=  new ArrayList[MAx];
        initG();//list数组里每一个都要初始化一下称为一个小的list,不然不能用
        n = sc.nextInt();
        intputtree(n);

        int root = sc.nextInt();
        p[root] = -1;
        dfs(root, p[root]);
        for(int i = 0;i<n;i++){
            System.out.print(p[i]+" ");
        }
    }

static void dfs(int u ,int fa){
        for(int i = 0;i<g[u].size();i++){
            Integer child = g[u].get(i);
            if(child==fa) continue;//这是啥,就这一行,没有他就全报stackoverflow!!!因为会死循环的!!
            dfs(child,u);
        }
        p[u] = fa; //这里应该是对的,,
    }

    static void initG(){
        for(int i=0;i<n;i++){
            g[i] = new ArrayList<Integer>();
        }
    }
    static void intputtree(int n){

        int u,v;
        for(int i = 0;i<n-1;i++){
            u  = sc.nextInt();
            v = sc.nextInt();
            g[u].add(v);
            g[v].add(u);
        }
    }
}

不太理解
输入:

8

0 1

0 2

0 3

1 4

1 5

5 6

5 7

1

输出:

1 -1 0 0 1 1 5 5

例题:
6、

生命之树

在X森林里,上帝创建了生命之树。

他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,代表这个点的和谐值。
上帝要在这棵树内选出一个非空节点集S,使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, …, vk, b} 使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。

在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。

经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。但是由于 atm 不擅长计算,他不知道怎样有效的求评分。他需要你为他写一个程序来计算一棵树的分数。

「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。由于这是一棵树,所以是不存在环的。

「输出格式」
输出一行一个数,表示上帝给这棵树的分数。

「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5

「样例输出」
8

「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。

这个问题,,首先是个什么问题???

tip;题目很明显的说这是一棵树了,,而且有n个顶点,就是接下来有n-1行,因为一棵树有n个顶点,就会有n-1条边的。注意啊,这里知道是树思路就清晰很多了

树形dp我也真是666了

第一种呢,暴力:枚举所有的子集,然后每个子集用dfs都去看是否能够达到所有点都能联通

第二种,比较神奇了,无根树转有根树的递归

首先,无根树转化为有根树,在每一个有根树中,这颗树的子树如何加和大于0,那就会对他产生正向的影响,如果小于0,那就不要那棵树了,但是这里系统的想一下分成的情况:1,要哪个根,前提是那个根是大于0的,那这个根下的所有子树就开始递归就好了  2,不要那个根,没有那个根就感觉变成了森林一样,,在所有森林中找个最大就出来了,同样每课小树都用递归就好了。。神奇的地方在这里:::无根树变有根树,每次选不同的点起始结果是一样的。最终结果是一样的。

但是这里的递归,因为一直递归到了叶子节点,然后从叶子开始计数,所有就相当于一个记忆性的东西了,要不要再转成一个树状dp都可以

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
 
class test6{
    static int w[];
    static List<Integer> []g;
    static int n;
    static int ans = 0;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n =  sc.nextInt();
        g = new ArrayList[n+1];
        initG();//list数组里每一个都要初始化一下称为一个小的list,不然不能用
        w = new int[n+1];
        for(int i =1;i<=n;i++){
            w[i] = sc.nextInt();
        }
        for(int i=0;i<n-1;i++){
            int a = sc.nextInt();
            int b = sc.nextInt();//下来就用邻接表来存吧,起始就是list,跟他连,被连的就是childrena
            g[a].add(b);
            g[b].add(a);
        }
        dfs(1,0);
        System.out.println(ans);
    }
    static void dfs(int u,int fa){
        for(int i = 0;i<g[u].size();i++){
            Integer child = g[u].get(i);
          if(child==fa) continue;//这是啥,就这一行,没有他就全报stackoverflow!!!因为会死循环的!!
            dfs(child,u);
            if(w[child]>0){
                w[u]+=w[child];//这儿比较巧妙,因为直接就存在了w[i]那个数组,因为这里dfs会一下子扎到最根处,也就是一直到最后的叶子节点,这里如果u加叶子节点大于0,那就要他,直接写到他那里就可以了
            }
            if(w[u]>ans){
                ans = w[u];
            }
        }
    }
    static void initG(){
        for(int i=0;i<n+1;i++){
            g[i] = new ArrayList<Integer>();
        }
    }
}

代码有一个小小的问题,就是应该把int改成long,但是我不知道为啥,明明数据范围也只到10^5,而引用一个大佬说的话:long型数据存储的数范围广而大 我给你举个例子
比方说 int只能存215612316这么大的数 long就可以存 121132131546523123156123这么大的数

?????为什么呀

第三种:这种是我能懂的办法无疑了,直接把当成图来做了

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
 
class test6{
    static int w[];
    static List<Integer> []g;
    static int n;
    static int ans = 0;
    static int [][]arr;
    static boolean vis[];
    static int  max = 0;
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        n =  sc.nextInt();
        arr = new int[n+1][n+1];
        w = new int[n+1];
        vis = new boolean[n+1];
        for(int i =1;i<=n;i++){
            w[i] = sc.nextInt();
        }
        for(int i=0;i<n-1;i++){
            int a = sc.nextInt();
            int b = sc.nextInt();//下来就用邻接表来存吧,起始就是list,跟他连,被连的就是childrena
           arr[a][b] = 1;
           arr[b][a] =1;
 
        }
        dfs(1);
        System.out.println(max);
    }
    static void dfs(int m ){
        vis[m] = true;
        for(int i=1;i<=n;i++){
            if(vis[i] ==false && arr[m][i] !=0){
                dfs(i);
                if(w[m]<(w[m]+w[i])){
                    w[m] = w[m]+w[i];
                }
                max = Math.max(max,w[m]);
            }
        }
 
    }
    static void initG(){
        for(int i=0;i<n+1;i++){
            g[i] = new ArrayList<Integer>();
        }
    }
}

第四种:树形dp

首先,树形dp是啥意思嘞??

先来一道例题:

二叉苹果树,

然后再写树形dp的代码,,,这里 我还没有看懂,,,,,

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值