java-实战: 黑白树 > 47746328(深度优先遍历)

题目描述

一棵n个点的有根树,1号点为根,相邻的两个节点之间的距离为1。树上每个节点i对应一个值k[i]。每个点都有一个颜色,初始的时候所有点都是白色的。
你需要通过一系列操作使得最终每个点变成黑色。每次操作需要选择一个节点i,i必须是白色的,然后i到根的链上(包括节点i与根)所有与节点i距离小于k[i]的点都会变黑,已经是黑的点保持为黑。问最少使用几次操作能把整棵树变黑。

输入描述:

第一行一个整数n (1 ≤ n ≤ 10^5) 接下来n-1行,每行一个整数,依次为2号点到n号点父亲的编号。 最后一行n个整数为k[i] (1 ≤ k[i] ≤ 10^5) 样例解释: 对节点3操作,导致节点2与节点3变黑 对节点4操作,导致节点4变黑 对节点1操作,导致节点1变黑

输出描述:

一个数表示最少操作次数

示例1

输入

4
1
2
1
1 2 2 1

输出

3

题解:
一开始以为是红黑树的姐妹黑白树。。
求出最少的操作,用dfs

我们要不断更新染色的最远距离,还要把子节点的染色范围更新的父亲节点
比如1->2->3-.>4->5->6->7
f[2]=5,f[3]=2
2节点就可以直接染色到6
操作完2之后,如果3就已经被染色了,如果3能染色的范围比fa[ 2 ]-1(因为2已经染色了本身,所以减一)还大,那染色范围可以更远
如果fa[3]<fa[2]-1,就把f3的最远距离更新到fa[2]-1
因为染色都是从下向上的。如果一个节点没办法被它子树的节点染色,那这个节点的父亲节点也没办法将它染色,他只能自己染色了
 

package com.example;

import java.util.*;

public class Main5{
    //声明节点个数为n
    static int n;
    //声明节点i的父节点为parent[i]
    static int[] parent;
    //声明节点i的染色距离为k[i]
    static int[] k;
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        //定义n为输入节点的个数为(n-1)
        n = in.nextInt();
        //定义父节点数组
        parent = new int[n];
        //声明颜色数组
        isBlack = new boolean[n];
        parent[0] = -1;
        for(int i =1;i<n;i++){
            //其实节点默认为2,此处为2-1。默认节点的父节点in.nextInt()-1
            int t = in.nextInt()-1;
            //默认节点的父节点存入parent数组当中。
            parent[i] = t;
        }
        k = new int[n];
        for(int i = 0;i < n;i++){
            //输入节点1到节点n的涂色距离(节点已经默认-1,所以此处为k[0]开始。)
            k[i] = in.nextInt();
        }
        // 第一次遍历,自底向上 dp 出每个子树的可延拓长度
        dp1();
        // 第二次遍历,赋值颜色,并记录操作次数
        System.out.println(dp2());
    }
    //声明颜色数组
    static boolean[] isBlack;

    static int dp2(){
        int result = 0;
        for(int i = n -1;i>=0;i--)
            //如果i没有被染色
            if(!isBlack[i]){
                //节点染色的距离赋值为t
                int t = k[i];
                //节点赋值为now
                int now = i;
                //如果染色距离t-1>0 该节点存在父节点
                while(t-- > 0 && now != -1){
                    //染色
                    isBlack[now] = true;
                    //当前节点赋值为父节点
                    now = parent[now];
                }
                result++;
            }
        return result;
    }
    static void dp1(){
        //将节点由高向低遍历
        for(int i = n-1;i>0;i--){
            //节点染色的距离赋值为t
            int t = k[i];
            //节点的父节点赋值为now
            int now = parent[i];
            //如果染色距离>0 该节点存在父节点
            while(--t > 0 && now != -1){
                //父节点的染色距离<该节点的染色距离 while判断条件执行后执行(--t) 此处t=t-1
                if(k[now] < t){
                    //把父节点的染色距离赋值为当前节点的染色距离
                    k[now] = t;
                    //把i父节点赋值为父节点的父节点
                    now = parent[now];
                }else break;
            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值