CCF-CSP 202312-3 树上搜索(Java、C++、Python)

文章目录

  • 树上搜索
    • 题目背景
    • 问题描述
    • 输入格式
    • 输出格式
    • 样例1输入
    • 样例1输出
    • 样例解释
    • 子任务
  • 满分代码
    • Java
    • C++
    • Python

树上搜索

题目背景

西西艾弗岛大数据中心为了收集用于模型训练的数据,推出了一项自愿数据贡献的系统。岛上的居民可以登录该系统,回答系统提出的问题,从而为大数据中心提供数据。为了保证数据的质量,系统会评估回答的正确性,如果回答正确,系统会给予一定的奖励。

近期,大数据中心需要收集一批关于名词分类的数据。系统中会预先设置若干个名词类别,这些名词类别存在一定的层次关系。例如,“动物”是“生物”的次级类别,“鱼类”是“动物”的次级类别,“鸟类”是“动物”的次级类别,“鱼类”和“鸟类”是“动物”下的邻居类别。这些名词类别可以被按树形组织起来,即除了根类别外,每个类别都有且仅有一个上级类别。

并且所有的名词都可以被归类到某个类别中,即每个名词都有且仅有一个类别与其对应。一个类别的后代类别的定义是: 若该类别没有次级类别,则该类别没有后代类别;否则该类别的后代类别为该类别的所有次级类别,以及其所有次级类别的后代类别。

下图示意性地说明了标有星号的类别的次级类别和后代类别。

在这里插入图片描述

次级类别与后代类别

系统向用户提出问题的形式是:某名词是否属于某类别,而用户可以选择“是”或“否”来回答问题。该问题的含义是:某名词是否可以被归类到某类别或其后代类别中。

例如,要确定名词“鳕鱼”的类别,系统会向用户提出“鳕鱼是否属于动物”,当用户选择“是”时,系统会进一步询问“鳕鱼是否属于鱼类”,当用户选择“是”时,即可确定“鳕鱼”可以被归类到“鱼类”这一类别。

此外,如果没有更具体的分类,某一名词也可以被归类到非叶子结点的类别中。例如,要确定“猫”的类别,系统可以向用户提出“猫是否属于动物”,当用户选择“是”时,系统会进一步分别询问“猫”是否属于“鱼类”和“鸟类”,当两个问题收到了否定的答案后,系统会确定“猫”的类别是“动物”。

大数据中心根据此前的经验,已经知道了一个名词属于各个类别的可能性大小。为了用尽量少的问题确定某一名词的类别,大数据中心希望小 C 来设计一个方法,以减少系统向用户提出的问题的数量。

问题描述

小 C 观察了事先收集到的数据,并加以统计,得到了一个名词属于各个类别的可能性大小的信息。具体而言,每个类别都可以赋予一个被称为权重的值,值越大,说明一个名词属于该类别的可能性越大。由于每次向用户的询问可以获得两种回答,小 C 联想到了二分策略。他设计的策略如下:

  1. 对于每一个类别,统计它和其全部后代类别的权重之和,同时统计其余全部类别的权重之和,并求二者差值的绝对值,计为 w δ w_{\delta} wδ
  2. 选择 w δ w_{\delta} wδ 最小的类别,如果有多个,则选取编号最小的那一个,向用户询问名词是否属于该类别;
  3. 如果用户回答“是”,则仅保留该类别及其后代类别,否则仅保留其余类别;
  4. 重复步骤 1,直到只剩下一个类别,此时即可确定名词的类别。

小 C 请你帮忙编写一个程序,来测试这个策略的有效性。你的程序首先读取到所有的类别及其上级次级关系,以及每个类别的权重。你的程序需要测试对于被归类到给定类别的名词,按照上述策略提问,向用户提出的所有问题。

输入格式

从标准输入读入数据。

输入的第一行包含空格分隔的两个正整数 n n n m m m,分别表示全部类别的数量和需要测试的类别的数量。所有的类别从 1 到 n n n 编号,其中编号为 1 的是根类别。

输入的第二行包含 n n n 个空格分隔的正整数 w 1 , w 2 , … , w n w_1,w_2,\ldots,w_n w1,w2,,wn,其中第 i i i 个数 w i w_i wi 表示编号为 i i i 的类别的权重。

输入的第三行包含 ( n − 1 ) (n-1) (n1) 个空格分隔的正整数 p 2 , p 3 , … , p n p_2,p_3,\ldots,p_n p2,p3,,pn,其中第 i i i 个数 p i + 1 p_{i+1} pi+1 表示编号为 ( i + 1 ) (i+1) (i+1) 的类别的上级类别的编号,其中 p i ∈ [ 1 , n ] p_i\in[1,n] pi[1,n]

接下来输入 m m m 行,每行一个正整数,表示带要测试的类别编号。

输出格式

输出 m m m 行,每行表示对一个被测试的类别的测试结果。表示按小 C 的询问策略,对属于给定的被测类别的名词,需要依次向用户提出的问题。

每行包含若干空格分隔的正整数,每个正整数表示一个问题中包含的类别的编号,按照提问的顺序输出。

样例1输入

5 2
10 50 10 10 20
1 1 3 3
5
3

样例1输出

2 5
2 5 3 4

样例解释

上述输入数据所表示的类别关系如下图所示,同时各个类别的权重也标注在了图上。

在这里插入图片描述
样例输入数据所表示的类别关系

对于归类于类别 5 的某个名词,按照上述询问策略,应当对于树上的每个节点,都计算 w δ w_{\delta} wδ 的值,对于类别 1 至 5,得到的 w δ w_{\delta} wδ 分别为:100、0、20、80、60。因此首先就类别 2 提问。由于类别 5 不属于类别 2 的后代类别,因此用户回答“否”,此时去除类别 2 和其全部后代类别,仅保留类别 1、3、4、5。对于剩下的类别,计算 w δ w_{\delta} wδ 的值,得到的 w δ w_{\delta} wδ 分别为:50、30、30、10。因此再就类别 5 提问。由于类别 5 就是被提问的名词所属类别,因此用户回答“是”,此时仅保留类别 5 和其全部后代类别。我们发现,这个时候,只剩下类别 5,因此算法结束。上述过程如下图所示:

在这里插入图片描述算法执行过程 1

对于归类于类别 3 的某个名词,按照上述询问策略,依次对类别 2、5 提问,过程与前述一致。但是由于类别 3 不属于类别 2 的后代类别,用户回答“否”,此时应当去掉类别 5 和其后代类别,仅保留类别 1、3、4。分别计算 w δ w_{\delta} wδ 得:30、10、10。此时应当选择编号较小的类别 3 提问。由于类别 3 就是被提问的名词所属类别,因此用户回答“是”,此时仅保留类别 3 和其全部后代类别。我们发现,这个时候,并非只剩下一个类别,因此算法还应继续进行。剩下的类别有 3、4,分别计算 w δ w_{\delta} wδ 得:20、0。因此再就类别 4 提问。由于类别 3 不属于类别 4 的后代类别,用户回答“否”,此时应当去掉类别 4 和其后代类别,仅保留类别 3。我们发现,这个时候,只剩下类别 3,因此算法结束。上述过程如下图所示:

在这里插入图片描述
算法执行过程 2

子任务

对 20% 的数据,各个类别的权重相等,且每个类别的上级类别都是根类别;

对另外 20% 的数据,每个类别的权重相等,且每个类别至多有一个下级类别;

对 60% 的数据,有 n ≤ 100 n\leq100 n100,且 m ≤ 10 m\leq10 m10

对 100% 的数据,有 n ≤ 2000 , m ≤ 100 n\leq2000,\quad m\leq100 n2000,m100, 且 w i ≤ 1 0 7 w_i\leq10^7 wi107

满分代码

树结构的模拟题,这题直接深度优先暴力搜索就能过了,使用记忆化搜索应该能更快一点。

总体思路是:

  1. 通过深度优先搜索,将目标类别(从根类别 1 开始)的权重更新为其全部后代类别的权重之和;
  2. 选择权值与其余全部可选的类别的权重之和的差最小的。如果有多个,则选取编号最小的那一个,输出该类别;
  3. 通过深度优先搜索,判断是否属于该类别;
  4. 如果属于,则仅保留该类别及其后代可选择的类别,将目标类别换成该类别
  5. 否则保留其余类别,即删除该类别及其所有后代类别;
  6. 重复上述步骤,逐步缩小搜索范围,直到只剩下一个类别,此时即可确定名词的类别。

Java 和 C++ 的代码是满分,Python 代码只有 80 分,逻辑上是一样的还没找到那 20 分是哪里的错误

在这里插入图片描述

Java

import java.util.*;
import java.io.*;

public class Main {
    static long[] wi, temp;
    static List<Integer>[] ls;
    static Set<Integer> yes = new HashSet<>();
    static Set<Integer> no = new HashSet<>();

    public static void main(String[] args) throws IOException {
        QuickInput in = new QuickInput();
        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
        int n = in.nextInt(), m = in.nextInt();
        temp = new long[n + 1];
        ls = new List[n + 1];
        for (int i = 1; i <= n; i++) temp[i] = in.nextInt();
        for (int i = 1; i <= n; i++) ls[i] = new ArrayList<>();
        for (int i = 2; i <= n; i++) ls[in.nextInt()].add(i);
        for (int i = 0; i < m; i++) {
            int t = in.nextInt(), pre = 1;
            no.clear();
            while (true) {
                wi = Arrays.copyOf(temp, n + 1);
                yes.clear();
                dfs(pre);
                if (yes.size() == 1) break;
                int idx = getMinIdx(pre);
                if (check(idx, t)) {
                    pre = idx;
                } else {
                    no.add(idx);
                }
                out.print(idx + " ");
            }
            out.println();
        }
        out.flush();
    }

    public static void dfs(int pre) {
        yes.add(pre);
        for (int i : ls[pre]) {
            if (!no.contains(i)) {
                dfs(i);
                wi[pre] += wi[i];
            }
        }
    }

    public static int getMinIdx(int pre) {
        long min = Long.MAX_VALUE;
        int idx = 1;
        for (int j : yes) {
            long wj = Math.abs(wi[pre] - 2 * wi[j]);
            if (min > wj) {
                min = wj;
                idx = j;
            }
        }
        return idx;
    }

    public static boolean check(int idx, int t) {
        if (idx == t) return true;
        for (int i : ls[idx]) {
            if (no.contains(i)) continue;
            if (check(i, t)) return true;
        }
        return false;
    }

    static class QuickInput {
        StreamTokenizer input = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        int nextInt() throws IOException {
            input.nextToken();
            return (int) input.nval;
        }
    }
}

C++

#include<bits/stdc++.h> 
using namespace std;

vector<long long> wi, temp;
vector<vector<int>> ls;
set<int> yes, no;

void dfs(int pre) {
    yes.insert(pre);
    for (int i : ls[pre]) {
        if (no.find(i) == no.end()) {
            dfs(i);
            wi[pre] += wi[i];
        }
    }
}

int getMinIdx(int pre) {
    long long minDiff = LLONG_MAX;
    int idx = 1;
    for (int j : yes) {
        long long wj = abs(wi[pre] - 2 * wi[j]);
        if (minDiff > wj) {
            minDiff = wj;
            idx = j;
        }
    }
    return idx;
}

bool check(int idx, int t) {
    if (idx == t) return true;
    for (int i : ls[idx]) {
        if (no.find(i) != no.end()) continue;
        if (check(i, t)) return true;
    }
    return false;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    int n, m;
    cin >> n >> m;
    temp.resize(n + 1);
    ls.resize(n + 1);
    for (int i = 1; i <= n; i++) cin >> temp[i];
    for (int i = 1; i <= n; i++) ls[i] = vector<int>();
    for (int i = 2; i <= n; i++) {
        int parent;
        cin >> parent;
        ls[parent].push_back(i);
    }

    for (int i = 0; i < m; i++) {
        int t, pre = 1;
        cin >> t;
        no.clear();
        while (true) {
            wi = temp;
            yes.clear();
            dfs(pre);
            if (yes.size() == 1) break;
            int idx = getMinIdx(pre);
            if (check(idx, t)) pre = idx;
            else no.insert(idx);
            cout << idx << " ";
        }
        cout << endl;
    }

    return 0;
}

Python

def dfs(pre):
    yes.add(pre)
    [dfs(i) for i in ls[pre] if i not in no]
    wi[pre] += sum(wi[i] for i in ls[pre] if i not in no)

def check(idx, t):
    return idx == t or any(check(i, t) for i in ls[idx] if i not in no)

n, m = map(int, input().split())
temp = [0] + list(map(int, input().split()))
ls = [[] for _ in range(n + 1)]
[ls[pa].append(i) for i, pa in enumerate(map(int, input().split()), 2)]

for _ in range(m):
    t, pre, no = int(input()), 1, set()
    while True:
        wi, yes = temp.copy(), set()
        dfs(pre)
        if len(yes) == 1:
            break
        idx = min(yes, key=lambda j: abs(wi[pre] - 2 * wi[j]))
        if check(idx, t):
            pre = idx
        else:
            no.add(idx)
        print(idx, end=" ")
    print()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/359829.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在 Amazon EKS 上部署生成式 AI 模型

导言 生成式 AI 正在改变企业的运作方式&#xff0c;并加快创新的步伐。总体而言&#xff0c;人工智能正在改变企业利用技术的方式。生成式 AI 技术包括微调和部署大型语言模型&#xff08;LLM&#xff09;&#xff0c;并允许开发人员访问这些模型以执行提示和对话。负责在 Kub…

搭建WebGL开发环境

前言 本篇文章介绍如何搭建WebGL开发环境 WebGL WebGL的技术规范继承自免费和开源的OpenGL ES标准&#xff0c;从某种意义上说&#xff0c;WebGL就是Web版的OpenGL ES&#xff0c;而OpenGL ES是从OpenGL中派生出来的。他们的应用环境有区别&#xff0c;一般来说&#xff1a;…

聊聊百度造车

10月27日&#xff0c;极越-01上市&#xff0c;一个月后大幅降价&#xff0c;时至今日距离发布已经过去了两个月&#xff0c;官方迟迟不肯公布销量&#xff0c;实际情况大家也都心知肚明。 如今小米汽车技术发布会风头无两&#xff0c;而同一年宣布造车的极越却无人问津&#x…

算法随想录第四十八天打卡| 198.打家劫舍 , 213.打家劫舍II , 337.打家劫舍III

详细布置 今天就是打家劫舍的一天&#xff0c;这个系列不算难&#xff0c;大家可以一口气拿下。 198.打家劫舍 视频讲解&#xff1a;动态规划&#xff0c;偷不偷这个房间呢&#xff1f;| LeetCode&#xff1a;198.打家劫舍_哔哩哔哩_bilibili 代码随想录 class Solution(…

【智能家居入门3】(MQTT服务器、MQTT协议、微信小程序、STM32)

前面已经写了三篇博客关于智能家居的&#xff0c;服务器全都是使用ONENET中国移动&#xff0c;他最大的优点就是作为数据收发的中转站是免费的。本篇使用专门适配MQTT协议的MQTT服务器&#xff0c;有公用的&#xff0c;也可以自己搭建&#xff08;应该要钱&#xff09;&#xf…

华为配置ARP安全综合功能实验

配置ARP安全综合功能示例 组网图形 图1 配置ARP安全功能组网图 ARP安全简介配置注意事项组网需求配置思路操作步骤配置文件 ARP安全简介 ARP&#xff08;Address Resolution Protocol&#xff09;安全是针对ARP攻击的一种安全特性&#xff0c;它通过一系列对ARP表项学习和A…

Qt应用开发(安卓篇)——调用ioctl、socket等C函数

一、前言 在 Qt for Android 中没办法像在嵌入式linux中一样直接使用 ioctl 等底层函数&#xff0c;这是因为因为 Android 平台的安全性和权限限制。 在 Android 中&#xff0c;访问设备硬件和系统资源需要特定的权限&#xff0c;并且需要通过 Android 系统提供的 API 来进行。…

在线版XD,免费使用,功能全面,设计神器!

Adobe XD是什么软件&#xff1f; Adobe XD软件是一个结合设计和建立原型功能的一站式UX/UI设计平台。在XD软件中&#xff0c;数字团队可以进行移动应用、网页设计和原型制作。与此同时&#xff0c;Adobe XD软件也是一种跨平台设计产品&#xff0c;结合设计和建立原型功能&…

力扣 122.买卖股票的最佳时机 II

代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {if(prices.size()1) return 0;int res 0;int i0;while(i<prices.size()-1){int ji1;if(prices[j]>prices[i]){//在找到对应元素的下一个元素比他大的时候买入while(j1 < p…

研学活动报名平台源码开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景 研学活动报名平台旨在为活动组织者提供方便快捷的研学活动管理工具&#xff0c;同时为用户提供全面的活动搜索、报名和支付等功能。通过该系统&#xff0c;活动组织者能够更好地管理活动报名信息&#xff0c;用户也可…

系统架构设计师-22年-下午题目

系统架构设计师-22年-下午题目 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答&#xff0c;二、三、四、五题中任选两题作答 试题一 (25分) 说明 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。…

Unity之第一人称角色控制

目录 第一人称角色控制 &#x1f634;1、准备工作 &#x1f4fa;2、鼠标控制摄像机视角 &#x1f3ae;3、角色控制 &#x1f603;4.杂谈 第一人称角色控制 专栏Unity之动画和角色控制-CSDN博客的这一篇也有讲到角色控制器&#xff0c;是第三人称视角的&#xff0c;以小编…

【LeetCode】142. 环形链表 II

leetcode题目链接 142. 环形链表 II /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ typedef struct ListNode ListNode;ListNode *detectCycle(ListNode *head) {ListNode *slow head, *fast head;while (…

【读点论文】SPTS Single-Point Text Spotting

SPTS Single-Point Text Spotting ABSTRACT 现有的场景文本识别(即&#xff0c;端到端文本检测和识别)方法依赖于昂贵的边界框注释(例如&#xff0c;文本行&#xff0c;词级或字符级边界框)。我们首次证明&#xff0c;训练场景文本识别模型可以通过对每个实例的单点进行极低成…

Cmake编译Opencv3.3.1遇到有些文件无法下载的错误解决:

前言&#xff1a; 对于&#xff0c;opencv有些配置文件错误并未致命&#xff0c;所以&#xff0c;有错误也不影响后续的编译&#xff1a;但是&#xff0c;后引用如果要用&#xff0c;在回过头来还是要解决的。 问题表述&#xff1a; 比如&#xff0c;有些文件下载的错误&am…

uni-app在hbuilderx打开微信开发工具运行

一、运行设置配置微信开发者工具路径 运行-运行到小程序模拟器-运行设置 配置微信开发工具的安装路径&#xff08;可浏览文件位置选择&#xff09;&#xff1b;web服务器端口号在第二步骤获得&#xff1b; 二、打开微信开发者工具设置-安全设置 打开服务端口开关&#xff0…

qt中使用mysql 数据库

QT 版本介绍 虽然版本是这个&#xff0c;但是工作目录确是&#xff1a; 下面陈述安装步骤 第一步&#xff1a; 就是安装MYSQL 数据库&#xff0c;在此不再赘述了&#xff0c;很多博主已经上传了。 第二步&#xff1a; 就是拷贝QT 对应mysql 的版本驱动到 QT 的编译器文件中…

华为VRP系统简介

因为现在国内主流是华为、华三、锐捷的设备趋势&#xff0c;然后考的证书也是相关的&#xff0c;对于华为设备的一个了解也是需要的。 一、VRP概述 华为的VRP(通用路由平台)是华为公司数据通信产品的通用操作系统平台&#xff0c;作为华为公司从低端到核心的全系列路由器、以太…

vue核心知识点

一、Vue基础知识点总结 开发vue项目的模式有两种&#xff1a; 基于vue.js&#xff0c;在html中引入vue.js&#xff0c;让vue.js管理div#app元素。基于脚手架环境&#xff1a;通过vue脚手架环境可以方便的创建一个通用的vue项目框架的模板&#xff0c;在此基础之上开发vue项目…

Flutter 点击空白处关闭软键盘,点击非TextField 关闭软键盘的方法

1&#xff1a;点击空白处(非控件上)关闭软键盘。 此方法有个问题&#xff0c;就是点击非空白区域&#xff0c;不会关闭软键盘&#xff0c;比如点击旁边的其他按钮&#xff0c;则软键盘还在。只适合点击空白处关闭软键盘 在 main.dart 入口 build 中增加 builder: (context, ch…