2024 ccfcsp认证打卡 2023 12-3 树上搜索

2024 ccfcsp认证打卡 2023 12-1
在这里插入图片描述

import java.util.*;

public class Main {
    static Scanner input = new Scanner(System.in);

    static class Node {
        int value;           // 节点的值
        int parent;          // 父节点的编号
        List<Integer> child; // 子节点列表

        Node() {
            child = new ArrayList<>();
        }
    }

    static Node[] a = new Node[2005]; // 存储处理后的树结构
    static Node[] b = new Node[2005]; // 存储处理前的树结构
    static long[] w = new long[2005]; // 节点权重
    static int n, m;                  // 节点数和查询次数

    public static void main(String[] args) {
        n = input.nextInt(); // 读取节点数
        m = input.nextInt(); // 读取查询次数

        // 读取每个节点的值和父节点信息,构建树结构
        for (int i = 1; i <= n; i++) {
            b[i] = new Node();
            b[i].value = input.nextInt(); // 读取节点值
        }

        for (int i = 2; i <= n; i++) {
            b[i].parent = input.nextInt(); // 读取父节点编号
            b[b[i].parent].child.add(i);   // 将当前节点加入父节点的子节点列表中
        }

        // 处理每次查询
        for (int i = 0; i < m; i++) {
            int q = input.nextInt(); // 当前查询的节点编号
            int root = 1; // 根节点初始为1

            // 复制b数组到a数组,用于每次查询的操作
            for (int j = 1; j <= n; j++) {
                a[j] = new Node();
                a[j].value = b[j].value;
                a[j].parent = b[j].parent;
                a[j].child = new ArrayList<>(b[j].child);
            }

            // 循环处理直到根节点的子节点为空
            while (!a[root].child.isEmpty()) {
                long total = addSum(root); // 计算根节点以及其后代节点的权重之和

                // 求出差值最小的节点编号作为下一个根节点
                solve(root, total);

                int tmp = find(root); // 找到差值最小的节点
                System.out.print(tmp + " "); // 输出当前根节点编号

                // 如果查询节点q是新的根节点的子孙节点,则更新根节点
                if (isChild(tmp, q)) {
                    root = tmp;
                } else { // 如果查询节点q不是新的根节点的子孙节点,则移除该节点及其后代节点
                    int index = a[tmp].parent;
                    a[index].child.remove(Integer.valueOf(tmp));
                }
            }
            System.out.println(); // 每次查询结束换行输出
        }
    }

    // 计算节点i以及其后代节点的权重之和
    static long addSum(int i) {
        long ans = a[i].value; // 初始值为节点的值
        for (int j = 0; j < a[i].child.size(); j++) {
            ans += addSum(a[i].child.get(j)); // 递归求和后代节点的值
        }
        return ans;
    }

    // 判断节点k是否是节点i的子孙节点
    static boolean isChild(int i, int k) {
        if (i == k) return true; // 如果节点相同,则k是i的子孙节点
        for (int j = 0; j < a[i].child.size(); j++) {
            if (isChild(a[i].child.get(j), k)) { // 递归判断子孙节点
                return true;
            }
        }
        return false;
    }

    // 递归计算节点i的权重w[i],并更新
    static void solve(int i, long total) {
        w[i] = Math.abs(2 * addSum(i) - total); // 计算节点i的权重
        for (int j = 0; j < a[i].child.size(); j++) {
            solve(a[i].child.get(j), total); // 递归计算子节点的权重
        }
    }

    // 找出i以及后代节点中w值最小的节点编号,若两者w值相同选最小的
    static int find(int i) {
        int minn = i; // 初始最小节点为i
        for (int j = 0; j < a[i].child.size(); j++) {
            int tmp = find(a[i].child.get(j)); // 递归找到子节点中权重最小的节点
            if (w[tmp] < w[minn] || (w[tmp] == w[minn] && minn > tmp)) {
                minn = tmp; // 更新最小节点编号
            }
        }
        return minn;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值