刷题笔记 - October 2023

一、填空题

1.12.5MB
【问题描述】在计算机存储中,12.5MB是多少字节?
【答案提交】13107200
12.5MB = 12.5 * 2^20 B = 13107200 B

【考察范围】考察了存储单位之间的换算关系,以及对这些单位的基本理解。这是计算机科学和信息技术中的基本概念之一,对于处理存储容量、文件大小和数据传输等任务非常重要。
通常,存储容量的单位有以下几种:字节 (Byte),千字节:1KB = 1,024字节,兆字节:1MB = 1,024KB,千兆字节:1GB = 1,024MB,万兆字节:1TB = 1,024GB。

2.最多边数
【问题描述】一个包含有2019个结点的有向图,最多包含多少条边?(不允许有重边)
【答案提交】4078362
最大边数 = 结点数量 × (结点数量 - 1) = 2019 × 2018 = 4,078,362

【考察范围】这个问题考察的范围涉及了图论中的基本概念,特别是有向图的基本性质。
① 有向图:图论中的一种图,其中图中的边是有方向的,即从一个结点指向另一个结点。
② 结点和边:结点是图中的点,边是连接结点的线段。题目中提到有2019个结点。
③ 最大边数:在一个有向图中,最大边数是一个常见问题。通常,不允许有重边。
④ 有向完全图:最大边数的计算基于有向完全图的概念。有向完全图是指每对不同的结点之间都存在一条有向边的有向图。

3.单词重排
【问题描述】将LANQIAO中的字母重新排列,可以得到不同的单词,如LANQIAO、AAILNOQ等,注意这7个字母都要被用上,单词不一定有具体的英文意义。请问,总共能排列多少个不同的单词。
【答案提交】2520
使用排列组合的公式,不考虑重复字母时,排列的总数是7的阶乘,即:7! = 5,040
但是,由于有两个重复的 "A",需要除以 "A" 的排列数,来纠正重复计数:7! / 2! = 2,520

【考察范围】这道题目的范围涵盖了排列和组合的基本概念,以及如何处理具有重复元素的排列问题。它是一个典型的数学和组合学问题,也在计算机科学中有广泛的应用,例如密码学和数据压缩。

4.括号序列
【问题描述】由1对括号,可以组成一种合法括号序列:()。由2对括号,可以组成两种合法括号序列:()()、(())。由4对括号组成的合法括号序列一共有多少种?
【答案提交】14
① 公式法:使用卡特兰数来计算合法括号序列的数量。C(n) = (2n)! / [(n + 1)! * n!] = 14;
② 枚举法:()()()()、(())()()、()(())()、()()(())、(())(())、((()))()、()((()))、(((())))、(()())()、()(()())、(()()())、((())())、(()(()))、((()()))。

【考察范围】这道题目的范围涵盖了括号匹配的概念以及卡特兰数的应用。括号匹配是计算机科学中用于解析和验证代码结构的重要概念,而卡特兰数在组合数学和计算机科学中也有广泛的应用,如树结构、路径计数和括号表达式等方面。

二、选择题

1.(判断题)小型机通常采用RISC和unix操作系统。(A)A.T B.F

小型机通常采用RISC(精简指令集计算)架构和UNIX操作系统。RISC架构通过减少每条指令的复杂性来提高数据处理效率,这在需要高性能和高可靠性的小型机环境中非常有价值。
同时,UNIX操作系统以其稳定性和强大的多任务处理能力而闻名,也是许多小型机和大型机的首选操作系统。因此,小型机通常会采用RISC和UNIX的组合以达到高效的运作。

2.(单选题)磁盘接口的类型包括:1、SAS;2、SATA;3、NL-SAS;4、SCSI;5、IDE。请对上述五种类型的接口速率由快到慢进行排序,正确的一项是?(B)
A.12345 B.13245 C.31254 D.21354

SAS(Serial Attached SCSI):SAS接口通常提供很高的数据传输速度,在10 Gbps或更高。
NL-SAS(Nearline SAS):NL-SAS是一种廉价的SAS变种,速度通常与SAS相似。
SATA(Serial ATA):SATA接口的速率通常较低,通常在1.5 Gbps、3 Gbps、或6 Gbps,具体取决于SATA的版本。
SCSI(Small Computer System Interface):SCSI接口的速率也可以很高,取决于具体的标准,通常在几百Mbps到几Gbps之间。
IDE(Integrated Drive Electronics):IDE接口是相对较旧的接口,速率相对较低,通常在几十Mbps到一百多Mbps之间,取决于具体的规范。

3.(判断题)传统的数据中心硬件包括X86服务器、小型机和大型机,存储和网络设备。(A)A.T B.F

传统的数据中心硬件确实包括X86服务器、小型机(通常是指主机型号较小的机架式服务器)和大型机(通常是指主机型号较大的机架式服务器、刀片服务器等),
以及存储和网络设备。这些硬件组成了传统数据中心的基础架构,用于支持各种计算、存储和网络需求。

4.(单选题)以下哪项用来确定路由优先级?(A)
A.路由协议算法的优劣 B.到目标网络的跳数 C.发送接口的带宽 D.物理链路的可靠性

路由优先级是指在路由表中选择最佳路径的标准。路由协议算法的优劣决定了路由选择的智能程度。不同的路由协议(如OSPF、EIGRP、RIP等)使用不同的算法来确定最佳路由。这些算法考虑了诸如带宽、成本、延迟、可靠性等因素,然后选择最佳路径。

5.(判断题)华为公司将网络和业务安全性保障的责任和公司的商业利益放在同等重要的位置。(B)A.T B.F

华为承诺将构筑并全面实施端到端的全球网络安全保障体系作为公司的重要发展战略之一,同时承诺将公司对网络和业务安全性保障的责任置于公司的商业利益之上。

6.(判断题)在shell命令行方式下,一行只能写一个指令,每次只能使用一个命令。(B)A.T B.F

在shell命令行下,你通常可以在一行中输入多个命令,这些命令可以使用分号(;)或双竖线(||)等符号来分隔。这允许你在同一行上依次运行多个命令,而不必等待前一个命令完成。这种能力对于自动化任务和批处理非常有用。

7.(判断题)将编译程序分成若干个“遍”是为了使程序的结构更加清晰。(A)A.T B.F

将编译程序分成多个遍是为了提高程序的结构清晰度和可维护性。编译过程通常包括多个阶段,每个阶段被称为一个遍。这种分阶段的方法有助于组织编译器的实现,使得每个阶段专注于特定的任务,提高了代码的可读性和可维护性。

8.(单选题)匹配"英文字母文件名:一个或者多个空格 行号"的正则表达式为:(C)
A.[a-Z]:+[ ]*[0-9]+
B.[a-zA-Z]:+[ ]+[0-9]*
C.[a-zA-Z]+:[ ]+[0-9]+
D.[a-Z]+:[ ]+[0-9]*

[a-zA-Z]+ 匹配一个或多个英文字母文件名。: 匹配冒号。[ ]+ 匹配一个或多个空格。[0-9]+ 匹配一个或多个数字表示行号。

9.(单选题)若要将当前目录中的myfile.txt文件压缩成myfile.txt.tar.gz,命令为(C)
A.tar -cvf myfile.txt myfile.txt.tar.gz
B.tar -zcvf myfile.txt myfile.txt.tar.gz
C.tar -zcvf myfile.txt.tar.gz myfile.txt
D.tar -cvf myfile.txt.tar.gz myfile.txt

压缩:tar -zcvf myfile.txt.tar.gz myfile.txt
解压:tar -zxvf myfile.txt.tar.gz myfile.txt
-z: 这个选项表示使用gzip压缩文件,创建一个.tar.gz文件。
-c: 这个选项表示创建一个新的归档文件。
-x: 这个选项表示提取一个已有归档文件。
-v: 这个选项表示在操作过程中显示详细信息。
-f myfile.txt.tar.gz: 这个选项指定了归档文件的名称,即myfile.txt.tar.gz。

10.(判断题)对于整数 n,(n & (n-1) ) == 0 的含义是判断 n 是否为偶数。(B)
A.正确 B.错误

表达式(n & (n-1)) == 0 的含义是判断 n 的二进制表示中是否只有一个位是置为1的,如果是的话,结果为真。如果 n 是2的幂,那么这个表达式就成立。并不代表 n 为偶数,而是代表 n 是2的幂。

11.(判断题)不含回路的有向图一定存在拓扑排序。(A)A.T B.F

不含回路的有向图一定存在拓扑排序。这是拓扑排序的一个重要性质。在不含回路的有向图中,任务之间的依赖关系是有序的,没有循环依赖。
拓扑排序是一种对有向无环图(DAG)进行排序的方法,其中每个顶点表示一个任务,有向边表示任务之间的依赖关系。

12.(单选题){0, 2, 1, 4, 3, 9, 5, 8, 6, 7} 是以数组形式存储的小顶堆,删除堆顶元素0后的结果是(D)
A.{2, 1, 4, 3, 8, 9, 5, 8, 6, 7}
B.{1, 2, 5, 4, 3, 9, 8, 6, 7}
C.{2, 3, 1, 4, 7, 9, 5, 8, 6}
D.{1, 2, 5, 4, 3, 9, 7, 8, 6}

小顶堆的性质是:对于堆中的任意非叶子节点i,都有其值小于或等于其孩子节点的值。
删除堆顶元素的操作步骤:将最后一个元素放到堆顶。对新堆顶的元素执行下沉操作,使之满足小顶堆的性质。

13.(单选题)20个节点的三叉树(每个节点都有三个指向孩子节点的指针),有多少个空指针?(C)A.40 B.39 C.41 D.60

对于一个普通的二叉树,总的指针数为2n,但只有n-1条边,所以空指针的数目是2n - (n-1) = n + 1。使用相同的逻辑,对于一个三叉树,空指针的数目 = 3n - (n-1) = 2n + 1。

14.(判断题)局部变量可以和全局变量重名,编译的时候不会出现错误,但一旦不小心,就可能导致使用错误变量,所以在定义局部变量的时候不要和全局变量重名。(A)A.T B.F

当局部变量与全局变量重名时,当你引用这个名称时,你实际上是在引用局部变量,而不是全局变量。在这个局部作用域之外,这个名称则会指向全局变量。
由于这个行为,很容易因为不小心而使用了错误的变量,特别是当程序很大,或者有很多嵌套的作用域时。这可能会导致出乎意料的行为和难以追踪的错误。

15.(单选题)关于数组初始化说法错误的是:(C)
A.在函数体外定义的内置数组,其元素均初始化为 0。
B.在函数体内定义的内置数组,其元素无初始化。
C.显式初始化的数组需要指定数组的维数值。
D.如果其元素为类类型,则自动调用该类的默认构造函数进行初始化。

A. 在C++中,全局或静态生命周期的内置数组,默认情况下,其所有元素都会被初始化为0。
B. 局部或自动存储周期的内置数组,默认情况下不会进行初始化,其元素的值是不确定的。
C. 在C++中,当你显式初始化一个数组时,你可以不指定数组的维数。编译器会根据提供的初始化列表自动推导出数组的维数。例如:int arr[] = {1, 2, 3}; // 维数为3,但并没有明确指定。
D. 这句话是说,当我们声明一个类对象数组而没有明确地初始化其元素时,那么这些元素会自动调用类的默认构造函数进行初始化。这是C++的一个规定。举个例子来说明:

#include <iostream>

class MyClass {
public:
    int data;
    // 默认构造函数
    MyClass() : data(0) {
        std::cout << "默认构造函数被调用!" << std::endl;
    }
};

int main() {
    // 声明一个MyClass对象的数组,大小为3
    MyClass arr[3];

    // 输出每个对象的data成员
    for (int i = 0; i < 3; i++) {
        std::cout << "arr[" << i << "].data = " << arr[i].data << std::endl;
    }

    return 0;
}

当你运行上面的代码时,你会看到以下输出:
默认构造函数被调用!
默认构造函数被调用!
默认构造函数被调用!
arr[0].data = 0
arr[1].data = 0
arr[2].data = 0

16.如下代码段中空白处应填入的代码正确的是(A)
A.it = v_obj.erase(it++);
B.v_obj.erase(it);
C.it = v_obj.erase(it); it++;
D.v_obj.erase(it++);

void delete_obj(std::vector &v_obj) {
    std::vector::iterator it = v_obj.begin();
    while(it != v_obj.end()) {
        if(!it->isEmpty()) {
            ______ //此处填代码
        }else {
            ++it;
        }
    }
}

A选项先用it删除当前元素,并将it递增到下一个元素,然后返回删除元素下一个的迭代器。
B选项只是简单地删除了it所指向的元素,但在删除之后,it已经无效了,下一次循环时可能会访问到无效的迭代器。
C选项当你调用erase时,它删除了当前的元素,并返回下一个元素的迭代器。接下来,你又增加了迭代器,这会跳过一个元素。
D选项与A有类似的作用,但是在C++中,对同一个值进行多次的副作用在一个单独的语句中是未定义的行为。因此,这个选项是不确定的。
代码的目的是在std::vector中删除非空对象。假设我们有一个vector,里面有以下元素:[A, B, C, D, E],并且我们有一个迭代器it指向B。

① 当我们调用v_obj.erase(it++)时,实际上发生了两件事情。
a. it++会让it指向B的下一个元素,也就是C。但在执行这个操作之前,它会先"记住"原来指向的位置(即B),以便于之后的erase操作。
b. 接着,erase会删除"记住的"位置的元素,也就是B。现在,队列中的元素是[A, C, D, E]。此时,erase完成后,返回一个新的迭代器,指向原来被删除元素的下一个位置,也就是C。
② 因为我们有it =,所以我们将新的迭代器的值赋给了it,使it仍然指向C。
现在,情境再次回到队列上,B已经离开队列,每个人都调整了自己的位置,填补了B离开后的空缺。而我们的迭代器it仍然指向C,准备继续遍历队列的下一个元素。

17.(多选题)Linux系统的优势有:(AB)
A.多用户多任务,使用者与群组的规划
B.稳定、高效和安全
C.实时操作系统
D.高游戏支持度

A. 多用户多任务,使用者与群组的规划:Linux系统是一个支持多用户多任务的操作系统。它允许多个用户同时访问计算机资源,并且能够有效地进行任务管理和资源分配。同时,Linux支持对使用者和群组进行规划,可以灵活地管理权限和资源访问。
B. 稳定、高效和安全:Linux以其稳定性、高效性和安全性而著称。相对于其他操作系统,Linux在服务器领域特别受欢迎,因为它能够持续运行数月甚至数年而无需重新启动。它还能够处理大量的工作负载而不会崩溃或出现明显的性能下降。此外,Linux的安全性能出色,因为它有一个活跃的开源社区持续更新和改进系统,以及及时修补潜在的漏洞和安全问题。
C. 实时操作系统:与C选项相比,Linux并非一个专门的实时操作系统。尽管有一些针对实时性能的Linux变体,但Linux的主要版本并不是被设计为专门的实时操作系统。
D. 高游戏支持度:相对于其他操作系统,Linux在游戏方面的支持度较低。尽管有一些游戏可以在Linux上运行,但仍然存在许多主流游戏不支持Linux。因此,D选项不是Linux系统的主要优势之一。

18.(多选题)下面关于哈弗曼树的叙述中,不正确的是:(ABD)
A.哈夫曼树一定是完全二叉树
B.哈夫曼树一定是平衡二叉树
C.哈夫曼树中权值最小的两个节点互为兄弟节点
D.哈夫曼树中左孩子节点小于父节点,右孩子节点大于父节点

A. 哈夫曼树(Huffman Tree)不一定是完全二叉树,但它是一种特殊类型的二叉树。
B. 虽然哈夫曼树通常是近似平衡的,但它们不需要满足平衡二叉树的强制性条件。
C. 在构建哈夫曼树的过程中,每次都选择权值最小的两个节点进行合并,因此这两个节点会成为兄弟节点。
D. 这是二叉搜索树(Binary Search Tree)的性质,但并不是哈夫曼树的性质。

19.(多选题)auto_ptr 类使用必须满足下列限制:(ABCD)
A.不要使用 auto_ptr 对象保存指向静态分配对象的指针。
B.不要使用两个 auto_ptrs 对象指向同一对象。
C.不要使用 auto_ptr 对象保存指向动态分配数组的指针。
D.不要将 auto_ptr 对象存储在容器中。

A. auto_ptr是一种智能指针,它会在其生命周期结束时自动删除分配的对象。然而,静态分配的对象不需要手动释放,因为它们在程序的整个生命周期内都存在。因此,使用auto_ptr来管理静态分配的对象会导致意外的内存释放,这可能会导致程序错误或崩溃。
B. auto_ptr具有所有权语义,当一个auto_ptr对象将其所有权传递给另一个对象时,原始对象的指针将被置为NULL。
这种特性导致了两个auto_ptr指向同一对象的问题,其中一个auto_ptr释放了对象,而另一个auto_ptr仍然认为它拥有对象的所有权。这会导致悬挂指针和未定义行为。
C. auto_ptr不是设计用来管理动态分配的数组的。当一个auto_ptr对象释放其拥有的对象时,它使用delete而不是delete[]。
对于动态分配的数组,应该使用std::unique_ptr或std::shared_ptr,或者更好地使用std::vector等标准库容器。
D. 当将auto_ptr对象存储在容器中时,容器可能会在某些操作中执行拷贝构造或赋值操作,这会导致拥有对象的所有权在多个auto_ptr之间转移。
这可能导致悬挂指针和未定义行为。为了在容器中安全地管理动态分配的对象,应该使用std::unique_ptr或std::shared_ptr。

20.(多选题)可以使用memset,memcpy直接进行初始化和拷贝的有:(ABD)
A.结构 B.枚举 C.类实例 D.指针

A.结构 (Structs) - 可以。因为结构通常由基本数据类型组成,可以进行字节级别的复制。
B.枚举 (Enums) - 从技术上讲,枚举是一个整数类型,所以它也可以使用memset和memcpy进行操作。但在实际应用中,我们通常不会这样做,因为这样的操作可能导致未定义的行为。
C.类实例 (Class instances) - 直接使用memset和memcpy来操作非POD类实例是不安全的。
D.指针 (Pointers) - 指针是基本的数据类型,所以可以使用memset来初始化(例如,将所有指针设置为NULL)并使用memcpy进行拷贝。

1.(判断题)Openstack中默认的hypervisor层是Xen。(B) A.T B.F

OpenStack中默认的hypervisor层并不是Xen。OpenStack是一个开源云计算平台,它允许管理和部署虚拟机实例以及其他云基础设施资源。
在OpenStack中,有多个支持的虚拟化技术和hypervisor选项,但默认的hypervisor通常是不同的,具体取决于OpenStack的发行版本和配置。
KVM是OpenStack中较常用的默认hypervisor。KVM是一种基于Linux内核的虚拟化技术,它提供了虚拟机管理和性能。

2.(单选题)Openstack组件中,下列哪一项可以提供镜像服务?(C)
A.Nova B.Neutron C.Glance D.Swift

Glance允许用户上传、查看和管理虚拟机镜像,这些镜像用于启动虚拟机实例。镜像是虚拟机的基础,包含了虚拟机的操作系统和应用程序。
Nova是OpenStack的计算服务,负责虚拟机的创建、启动和管理。
Neutron是OpenStack的网络服务,负责虚拟机实例的网络配置和管理。
Swift是OpenStack的对象存储服务,用于存储大规模的非结构化数据,而不是虚拟机镜像。

3.(判断题)HDFS是基于流数据模式访问和处理超大文件的需求而开发的,具有高容错、高可靠性、高可扩展性、高吞吐率等特征,适合的读写任务是一次写入,多次读取。(A) A.T B.F

HDFS(Hadoop Distributed File System)是设计用于存储和处理大规模数据的分布式文件系统,它通过将大文件划分成小的数据块,然后将这些数据块分布式存储在多个计算节点上,以实现高吞吐率和容错性。
在HDFS中,数据通常是一次写入,然后多次读取,这适用于大数据处理和分析工作负载,如Hadoop MapReduce任务。

4.(单选题)下列选项中,哪一项属于大数据的核心?(B)
A.告知与许可 B.预测 C.匿名化 D.规模化

大数据分析不仅关注对已有数据的描述性分析(如报表、汇总、可视化等),还包括了对未来趋势和模式的预测。通过分析大数据集,机器学习和数据挖掘技术可以用于构建模型,以预测未来事件或结果。
告知与许可:这与数据隐私和合规性相关,但不是大数据的核心。
匿名化:匿名化是为了保护个人隐私,通常在大数据处理中用到,但不是其核心目标。
规模化:规模化是大数据的一个特点,但它本身不是大数据的核心,而是指大规模数据的处理和存储。

5.(单选题)某超市研究销售纪录数据后发现,买啤酒的人很大概率也会购买尿布,这种情况属于数据挖掘的哪类问题?(C)
A.分类 B.聚类 C.关联规则发现 D.自然语言处理

当超市分析销售记录数据时,他们发现购买啤酒和购买尿布之间存在一种关联,即购买啤酒的人也很大概率购买尿布。这种关联规则有助于超市进行交叉销售和商品摆放策略的优化。
分类是一种数据挖掘问题,它涉及将数据点分为不同的类别或标签。
聚类是将数据点分成具有相似性的群组的问题,而不是寻找项之间的关联。
自然语言处理是处理和分析文本数据的领域,与该问题描述无关。

6.(单选题)下列选项中,哪个选项是用于处理海量数据的并行编程模式和大规模数据集的并行运算的软件架构?(B)
A.GFS B.MapReduce C.Chubby D.BitTable

MapReduce是由Google开发的,用于分布式计算。MapReduce将大规模数据集分解成小的数据块,然后并行处理这些数据块,最后将结果合并起来。这种并行处理方式使得处理海量数据变得高效,容错性强,适用于大规模数据分析任务。
GFS:GFS是Google开发的分布式文件系统,用于存储大规模数据,但它不是并行编程模式和计算架构。
Chubby:Chubby是Google开发的分布式锁服务,用于协调分布式系统中的进程,而不是用于处理大规模数据的计算。
BitTable:BitTable不是一个与处理大规模数据相关的软件架构或模式。

7.(判断题)人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。(A) A.T B.F

8.(判断题)机器学习是人工智能的核心研究领域之一,任何一个没有学习能力的系统都很难被认为是一个真正的智能系统。(A) A.T B.F

9.(单选题)以下是针对哪种机器学习算法的描述?(B)
系统从环境到行为映射的学习,以获得最大的数值的奖赏信号,然后进行闭路循环,与其他机器学习算法不同,它着重与从交互中进行以目标为导向的学习。
A.监督学习 B.强化学习 C.半监督学习 D.聚类学习

这些描述与强化学习的基本原理相吻合。强化学习的主要特点就是通过与环境的交互来学习策略,以获得最大的奖励。

10.(判断题)5G可以每平方公里支持1百万个连接。(A) A.T B.F

5G技术的目标之一是增加网络的连接密度,从而支持更多的设备在同一区域内连接。根据国际电信联盟(ITU)和3GPP的标准,5G目标是每平方公里支持100万个连接。

11.(单选题)下列哪一种云服务是把应用或数据作为一种服务交付给客户的?(A)
A.SaaS B.PaaS C.IaaS

A. SaaS (Software as a Service) - 这是一种将应用程序作为一种服务提供给客户的方法,用户可以通过互联网访问这些应用程序。常见包括Google Workspace、Microsoft Office 365等。
B. PaaS (Platform as a Service) - 它为客户提供一个平台,让开发者可以在上面开发、运行和管理应用程序,但用户并不直接使用这些应用程序。它主要关心的是应用程序的开发环境。
C. IaaS (Infrastructure as a Service) - 它提供的是基础设施服务,例如虚拟化的硬件、存储和网络资源。用户可以在这些资源上部署和运行任何软件,包括操作系统和应用程序。

12.(多选题)Openstack项目中提供块存储服务和对象存储服务的组件是下列哪两个?(CD)
A.Nova B.Keystone C.Cinder D.Swift

A. Nova - 这是OpenStack的计算组件,主要负责虚拟机实例的生命周期管理。
B. Keystone - 这是OpenStack的身份服务组件,为其他服务提供认证、授权和服务发现。
C. Cinder - 这是OpenStack的块存储服务组件,为虚拟机实例提供块设备,如硬盘。
D. Swift - 这是OpenStack的对象存储服务组件,提供了一个分布式、API可访问的存储平台,用于存储和检索大量不可变的数据对象,如备份、归档或静态web内容。

13.(多选题)以下哪些因素是人工智能发展迅速的必要因素?(AC)
A.硬件 B.人们对AI的态度 C.算力 D.给机器注入情感

A. 硬件 - 硬件是人工智能发展的基础。随着计算硬件(如GPU、TPU等)的发展和进步,机器学习和深度学习模型能够更快速地进行训练和推断。强大的硬件为复杂的模型提供了必要的支持,使其可以在实际环境中运行。
B. 人们对AI的态度 - 虽然人们的态度和接受度确实对AI的应用和推广有影响,但它不是AI发展迅速的必要因素。即使在人们持怀疑态度的环境中,技术也可能会发展和进步。
C. 算力 - 算力与硬件紧密相关。随着算力的增加,我们能够处理更大的数据集、训练更复杂的模型并更快地得到结果。算力的增加通常与硬件的发展并行,为AI的研究和应用提供了动力。
D. 给机器注入情感 - 这不是AI发展迅速的必要因素。虽然某些研究领域可能会探索机器情感和人工情感智能,但AI的主要进展并不依赖于机器的情感化。

14.(多选题)以下哪些频段可以用于5G?(ABC)
A.3.5GHZ B.900MHz C.39GHz D.150GHz

3.5GHz: 是5G网络中频段的一部分,特别是在很多国家,3.5GHz已被认为是5G主要的中频段。它结合了覆盖范围和速度的优势。
900MHz: 在某些国家和地区,900MHz频段可能被用于5G网络,尤其是在提供更广泛的覆盖范围,例如乡村地区。这个频段在4G LTE时代已经被使用,但可能重新分配或共享用于5G服务。
39GHz: 这是5G网络的毫米波部分,特别在美国,39GHz已被分配作为5G使用的频段。毫米波频段提供了非常高的数据传输速率,但其覆盖范围有限,更多地被用于高密度区域。
150GHz: 150GHz并没有被广泛地认为是5G使用的频段。而且,这个频段的波长更短,适合的应用场景可能有限。

15.(多选题)以下哪些是Massive MIMO带来的好处?(ABC)
A.3D赋型可以增强高楼的覆盖
B.MU-BF可以提升小区速率
C.Massive MIMO的波束更窄,可以降低干扰
D.Massive MIMO的天线重量可以大幅度降低,对部署要求降低

A. Massive MIMO (Massive Multiple Input Multiple Output) 通过使用大量的天线能够实现3D波束成形,这样可以针对性地发送或接收信号,包括垂直方向,从而改善对高楼或其他难以到达的地方的覆盖。
B. MU-BF (多用户波束成形,Multi-User Beamforming) 是Massive MIMO的一个关键特性。通过MU-BF,系统可以同时为多个用户在相同的时间和频率资源上发送数据,从而大大提高了小区的总体数据速率。
C. 由于Massive MIMO使用的天线数量较多,其波束成形能力增强,可以产生更窄的波束。这种窄波束不仅可以提高信号的接收质量,还可以降低对其他用户或邻近小区的干扰。
D. 这个说法是不准确的。虽然每个单独的天线元素可能较小,但Massive MIMO系统中包含的天线数量很多,所以整体上,它们的天线阵列可能相对较大和重。因此,对于部署和安装可能有更高的要求。

三、编程题

1.汽水瓶
某商店规定:三个空汽水瓶可以换一瓶汽水,允许向老板借空汽水瓶(但是必须要归还)。小张手上有n个空汽水瓶,她想知道自己最多可以喝到多少瓶汽水。
数据范围:输入的正整数满足 1≤n≤100
输入描述:输入文件最多包含 10 组测试数据,每个数据占一行,仅包含一个正整数 n( 1<=n<=100 ),表示小张手上的空汽水瓶数。n=0 表示输入结束,你的程序不应当处理这一行。
输出描述:对于每组测试数据,输出一行,表示最多可以喝的汽水瓶数。如果一瓶也喝不到,输出0。
输入例子:3 \n 10 \n 81 \n 0
输出例子:1 \n 5 \n 40

  • 我的答案
#include 
using namespace std;

int Bottle(int n){
    if(n == 1)
        return 0;
    else if(n == 2)
        return 1;
    else if(n >=3){
        int bottle = n/3;
        int temp = n/3 + n%3;
        bottle += Bottle(temp);
        return bottle;
    }
}

int main() {
    int i=0;
    int n;
    int result[100]={0};
    cin >> n;
    while(n!=0 && n>=1 && n<=100){
        result[i++]=Bottle(n);
        cin >> n;
    }
    for(int j=0; j
  • 标准答案
#include 
#include 

using namespace std;

int main() {
    vector test_cases;  // 用于存储所有测试数据

    // 循循环读入测试数据,直到遇到 n=0 表示输入结束
    while (true) {
        int n;
        cin >> n;
        if (n == 0)
            break;  // 输入结束,退出循环
        test_cases.push_back(n);
    }

    // 计算并一次性输出结果
    for (int n : test_cases) {
        int total_drinks = 0;
        int empty_bottles = n;
        
        while (empty_bottles >= 3) {
            int drinks = empty_bottles / 3;
            total_drinks += drinks;
            empty_bottles = drinks + (empty_bottles % 3);
        }
        if (empty_bottles == 2) 
            total_drinks += 1;
        cout << total_drinks << endl;
    }
    return 0;
}

std::vector 是 C++ 标准库提供的一个动态数组容器,它能够动态调整大小,非常灵活,用于存储一组元素。
① 声明和初始化:例如:std::vector numbers = {1, 2, 3, 4, 5};
② 添加元素: 可以使用 push_back 函数将元素添加到 vector 的末尾:numbers.push_back(6);
③ 访问元素: 可以使用下标运算符 [] 或 at() 函数来访问。int element = numbers[2];
④ 获取大小: 可以使用 size() 函数来获取 vector 中的元素数量。int size = numbers.size();
⑤ 遍历元素: 可以使用循环,如 for 循环或范围-based for 循环。for (int n : numbers)
⑥ 删除元素: 使用 erase() 函数删除 vector 中的元素。numbers.erase(numbers.begin() + 2);

2.明明的随机数
明明生成了N个1到500之间的随机整数。请你删去其中重复的数字,即相同的数字只保留一个,把其余相同的数去掉,然后再把这些数从小到大排序,按照排好的顺序输出。
数据范围: 1≤n≤1000,输入的数字大小满足 1≤val≤500。
输入描述:第一行先输入随机整数的个数 N 。接下来的 N 行每行输入一个整数,代表明明生成的随机数。
输出描述:输出多行,表示输入数据处理后的结果
输入例子:3 \n 2 \n 2 \n 1
输出例子:1 \n 2

  • 我的答案
#include 
#include 

using namespace std;

int main(){
    int n, temp, val;
    cin >> n;
    vector Array(n, 0);
        
    for(int i = 0; i < Array.size(); i ++) {
        cin >> val;
        if(1 <= val && val <= 500) {
            Array[i] = val;
        } else {
            cout << "error input!";
        }
    }
    for(int i = 0; i < Array.size(); i++) {
        for(int j = i + 1; j < Array.size(); j++) {
            if(Array[j] < Array[i]) {
                temp = Array[i];
                Array[i] = Array[j];
                Array[j] = temp;
            } else if(Array[j] == Array[i]){
                Array.erase(Array.begin() + j);
                j--;
            }
        }
    }
    for(int i = 0; i < Array.size(); i ++) {
        cout << Array[i] << endl;
    }
    return 0;
}
  • 标准答案
#include 
#include 

using namespace std;

int main() {
    int n, num;
    cin >> n;

    set Array;

    for (int i = 0; i < n; i++) {
        cin >> num;
        Array.insert(num);
    }

    for (int num : Array) {
        cout << num << endl;
    }

    return 0;
}

如果你需要去重、排序和高效查找操作,std::set可能更合适。
如果你需要频繁插入和删除、保持插入顺序或随机访问元素,std::vector可能更合适。
std::set 是有序的关联容器,而不是简单的数组,它不支持通过指定大小来初始化的方式。
std::set 是基于红黑树的数据结构,用于保持集合中的元素有序且唯一。

3.进制转换
写出一个程序,接受一个十六进制的数,输出该数值的十进制表示。
数据范围:保证结果在 1 ≤ n ≤ 2^31-1。
输入描述:输入一个十六进制的数值字符串。
输出描述:输出该数值的十进制字符串。不同组的测试用例用\n隔开。
输入例子:0xAA
输出例子:170

  • 我的答案
#include 
#include 
#include 
using namespace std;

int main() {
    string input;
    cin >> input;
    int output = 0, temp;
    for(int i = input.size()-1; i > 1; i --){
        temp = pow(16, input.size() - i - 1);
        if(input[i] >= '0' && input[i] <= '9')
            output += temp * (input[i] - '0');
        else
            output += temp * (input[i] - 'A' + 10);
    }
    cout << output;
    return 0;
}
  • 标准答案
#include 
#include 

using namespace std;

int main() {
    string input;
    while (cin >> input)
        cout << stoul(input, NULL, 16) << endl;
    return 0;
}

① std::stoul:用于将字符串转换为无符号长整型的函数。stoul 代表 "string to unsigned long"。
② nullptr:表示额外的转换选项。如果没有使用任何额外选项,就传递一个空指针。
③ 16:这是告诉函数 std::stoul 按照十六进制来解析输入字符串。

4.反倍数
【问题描述】给定三个整数 a, b, c,如果一个整数既不是 a 的整数倍也不是 b 的整数倍还不是 c 的整数倍,则这个数称为反倍数。请问在 1 至 n 中有多少个反倍数。
【输入格式】输入的第一行包含一个整数 n。第二行包含三个整数 a, b, c,相邻两个数之间用一个空格分隔。
【输出格式】输出一行包含一个整数,表示答案。
【样例输入】30 \n 2 3 6
【样例输出】10
【样例说明】以下这些数满足要求:1, 5, 7, 11, 13, 17, 19, 23, 25, 29。
【评测用例规模与约定】对于 40% 的评测用例,1 <= n <= 10000。
对于 80% 的评测用例,1 <= n <= 100000。
对于所有评测用例,1 <= n <= 1000000,1 <= a <= n,1 <= b <= n,1 <= c <= n。

  • 我的答案
#include 

using namespace std;

int main(){
    int a, b, c, n;
    int count = 0;
    cin >> n;
    cin >> a >> b >> c;
    for(int i = 1; i <= n ; i++){
        if(i % a != 0 & i % b != 0 & i % c != 0)
            count ++;
    }
    cout << count;
    return 0;
}

【考察范围】具备对条件判断和逻辑运算的理解和应用能力。

5.凯撒加密
【问题描述】给定一个单词,请使用凯撒密码将这个单词加密。凯撒密码是一种替换加密的技术,单词中的所有字母都在字母表上向后偏移3位后被替换成密文。即a变为d,b变为e,...,w变为z,x变为a,y变为b,z变为c。例如,lanqiao会变成odqtldr。
【输入格式】输入一行,包含一个单词,单词中只包含小写英文字母。
【输出格式】输出一行,表示加密后的密文。
【样例输入】lanqiao
【样例输出】odqtldr
【评测用例规模与约定】对于所有评测用例,单词中的字母个数不超过100。

  • 我的答案
#include 
#include 

using namespace std;

int main() {
    string word, encrypt;
    cin >> word;
    for (char c : word) {
        c = 'a' + (c - 'a' + 3) % 26; 
        encrypt += c;
    }
    cout << encrypt;
    return 0;
}

【考察范围】① 字符串处理:处理输入的单词,遍历字符串中的每个字符并对其进行替换。
② 凯撒密码加密算法:实现凯撒密码的加密过程,即将字母表中的每个字母向后偏移3位。

6.螺旋
【问题描述】对于一个 n 行 m 列的表格,我们可以使用螺旋的方式给表格依次填上正整数,我们称填好的表格为一个螺旋矩阵。例如,一个 4 行 5 列的螺旋矩阵如下:
01 02 03 04 05
14 15 16 17 06
13 20 19 18 07
12 11 10 09 08
【输入格式】输入的第一行包含两个整数 n, m,分别表示螺旋矩阵的行数和列数。第二行包含两个整数 r, c,表示要求的行号和列号。
【输出格式】输出一个整数,表示螺旋矩阵中第 r 行第 c 列的元素的值。
【样例输入】4 5 2 2
【样例输出】15
【评测用例规模与约定】对于 30% 的评测用例,2 <= n, m <= 20。
对于 70% 的评测用例,2 <= n, m <= 100。
对于所有评测用例,2 <= n, m <= 1000,1 <= r <= n,1 <= c <= m。

  • 我的答案
#include 

using namespace std;

int count = 1, m, n;

int** LeftMove(int** luoxuan, int r, int c);
int** RightMove(int** luoxuan, int r, int c);
int** UpMove(int** luoxuan, int r, int c);
int** DownMove(int** luoxuan, int r, int c);

int main() {
    int r, c;
    cin >> n >> m;
    cin >> r >> c;
    // 动态分配二维数组
    int** LuoXuan = new int*[n];
    for (int i = 0; i < n; i++) {
        LuoXuan[i] = new int[m];
    }
    // 初始化矩阵为零
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++)
            LuoXuan[i][j] = 0;
    }
    // 初始化螺旋矩阵
    LuoXuan = RightMove(LuoXuan, 0, 0);
    cout << LuoXuan[c-1][r-1];
    // 释放动态分配的内存
    for (int i = 0; i < n; i++) {
        delete[] LuoXuan[i];
    }
    delete[] LuoXuan;
    return 0;
}


int** LeftMove(int** luoxuan, int r, int c) {
    int j = c;
    while (j >= 0 && luoxuan[r][j] == 0) {
        luoxuan[r][j] = count++;
        j--;
    }
    if (r - 1 >= 0 && luoxuan[r - 1][j + 1] == 0) {
        return UpMove(luoxuan, r - 1, j + 1);
    } else if (r + 1 < n && luoxuan[r + 1][j + 1] == 0) {
        return DownMove(luoxuan, r + 1, j + 1);
    }
    return luoxuan;
}

int** RightMove(int** luoxuan, int r, int c) {
    int j = c;
    while (j < m && luoxuan[r][j] == 0) {
        luoxuan[r][j] = count++;
        j++;
    }
    if (r - 1 >= 0 && luoxuan[r - 1][j - 1] == 0) {
        return UpMove(luoxuan, r - 1, j - 1);
    } else if (r + 1 < n && luoxuan[r + 1][j - 1] == 0) {
        return DownMove(luoxuan, r + 1, j - 1);
    }
    return luoxuan;
}

int** UpMove(int** luoxuan, int r, int c) {
    int i = r;
    while (i >= 0 && luoxuan[i][c] == 0) {
        luoxuan[i][c] = count++;
        i--;
    }
    if (c + 1 < m && luoxuan[i + 1][c + 1] == 0) {
        return RightMove(luoxuan, i + 1, c + 1);
    } else if (c - 1 >= 0 && luoxuan[i + 1][c - 1] == 0) {
        return LeftMove(luoxuan, i + 1, c - 1);
    }
    return luoxuan;
}

int** DownMove(int** luoxuan, int r, int c) {
    int i = r;
    while (i < n && luoxuan[i][c] == 0) {
        luoxuan[i][c] = count++;
        i++;
    }
    if (c + 1 < m && luoxuan[i - 1][c + 1] == 0) {
        return RightMove(luoxuan, i - 1, c + 1);
    } else if (c - 1 >= 0 && luoxuan[i - 1][c - 1] == 0) {
        return LeftMove(luoxuan, i - 1, c - 1);
    }
    return luoxuan;
}
  • 标准答案
#include 
#include 

using namespace std;

int main() {
    int n, m, r, c;
    cin >> n >> m >> r >> c;
    // 初始化二维矩阵
    vector> matrix(n, vector(m, 0));

    int top = 0, bottom = n - 1, left = 0, right = m - 1;
    int num = 1;
    // 初始化螺旋矩阵
    while (top <= bottom && left <= right) {
        for (int j = left; j <= right; j++) {       // 从左到右,填充当前行的元素
            matrix[top][j] = num++;
        }
        top++;      // 更新上边界
        for (int i = top; i <= bottom; i++) {       // 从上到下,填充当前列的元素
            matrix[i][right] = num++;
        }
        right--;    // 更新右边界
        for (int i = right; i >= left; i--) {   // 从右到左,填充当前行的元素
            matrix[bottom][i] = num++;
        }
        bottom--;   // 更新下边界
        for (int i = bottom; i >= top; i--) {   // 从下到上,填充当前列的元素
            matrix[i][left] = num++;
        }
        left++;     // 更新左边界
    }
    cout << matrix[r - 1][c - 1];
    return 0;
}

【思路解析】创建一个 n 行 m 列的二维数组,用于表示螺旋矩阵。使用四个变量来跟踪当前的行和列的范围:top、bottom、left、right。初始时,top 为 0,bottom 为 n - 1,left 为 0,right 为 m - 1。使用一个变量 num 从 1 开始,表示当前要填充的数字。在一个循环中,按照螺旋的方式填充矩阵元素,同时更新 top、bottom、left、right 以缩小范围。当 num 达到 r 行 c 列所对应的位置时,即可输出该位置的元素值。
【考察范围】① 数组和矩阵操作:需要理解和处理二维数组(矩阵)的操作,包括如何初始化、遍历、填充以及获取特定位置的元素。
② 螺旋矩阵生成:需要理解如何按照螺旋顺序填充矩阵的元素。
③ 坐标变换:需要将给定的行号和列号转化为螺旋矩阵中的坐标,以便找到对应位置的元素。

7.摆动序列
【问题描述】如果一个序列的奇数项都比前一项大,偶数项都比前一项小,则称为一个摆动序列。即 a[2i]a[2i]。小明想知道,长度为 m,每个数都是 1 到 n 之间的正整数的摆动序列一共有多少个。
【输入格式】输入一行包含两个整数 m,n。
【输出格式】输出一个整数,表示答案。答案可能很大,请输出答案除以10000的余数。
【样例输入】3 4
【样例输出】14
【样例说明】以下是符合要求的摆动序列:2 1 2,2 1 3,2 1 4,3 1 2,3 1 3,3 1 4,3 2 3,3 2 4,4 1 2,4 1 3,4 1 4,4 2 3,4 2 4,4 3 4
【评测用例规模与约定】对于 20% 的评测用例,1 <= n, m <= 5;
对于 50% 的评测用例,1 <= n, m <= 10;
对于 80% 的评测用例,1 <= n, m <= 100;
对于所有评测用例,1 <= n, m <= 1000。

  • 我的答案
#include 

using namespace std;

int count = 0;
int m, n;

void Function(int last, int index) {
    if (index > m) {
        count ++;
        return;
    }
    if (index % 2 == 1) {                       // 当前为奇数项
        for(int i = last + 1; i <= n; i ++) {
            Function(i, index + 1);
        }
    }else {                                     // 当前为偶数项
        for(int i = 1; i <= last - 1; i ++) {
            Function(i, index + 1);
        }
    }
}

int main() {
    cin >> m >> n;
    for (int i = 2; i <= n; i++) {              // 第一项 a1 = 2 ~ n,同理,第二项 a2 = 1 ~ a1 - 1
        Function(i, 2);                         // 第三项 a3 = a2 + 1 ~ n,第四项 a4 = 1 ~ a3 - 1...                               
    }
    cout << count << endl;
    return 0;
}
  • 标准答案
#include 
#include 

using namespace std;

int Function(int m, int n) {
    // dp[i][j] 表示长度为i,以j结尾的摆动序列的数量
    vector> dp(m + 1, vector(n + 1, 0));
    // 长度为1,以j结尾的摆动序列的数量为 1
    for (int j = 1; j <= n; j ++) {
        dp[1][j] = 1;
    }
    // 动态规划填表
    for (int i = 2; i <= m; i ++) {
        // j 是当前项的结尾数,k 是上一项的结尾数
        for (int j = 1; j <= n; j ++) {     
            if (i % 2 == 0) {               // 当前是偶数项,k = j + 1 ~ n
                for (int k = j + 1; k <= n; k ++) {
                    dp[i][j] = (dp[i][j] + dp[i - 1][k]) % 10000;
                }
            } else {                        // 当前是奇数项,k = 1 ~ j - 1
                for (int k = 1; k < j; k ++) {
                    dp[i][j] = (dp[i][j] + dp[i - 1][k]) % 10000;
                }
            }
        }
    }
    int count = 0;
    for (int j = 1; j <= n; ++j) {
        count = (count + dp[m][j]) % 10000; // mod(10000) 防止溢出
    }
    return count;
}

int main() {
    int m, n;
    cin >> m >> n;
    cout << Function(m, n) << endl;
    return 0;
}

【思路解析】这个问题涉及计算摆动序列的数量,其中摆动序列是一个长度为m的序列,其中奇数项都比前一项大,偶数项都比前一项小,且每个数都在1到n之间。解决方法是使用动态规划。定义一个二维数组dp,其中dp[i][j]表示长度为i的序列的最大值为j的摆动序列的数量。然后通过递推关系式计算dp数组。
【考察范围】① 动态规划:需要使用动态规划来计算数量,涉及到状态转移和填表过程。
② 数学建模:将问题转化为数学模型,构建适当的状态和递推关系,是解决这类问题的关键。
③ 循环和条件语句:使用循环嵌套来遍历序列长度和结尾数,以及根据奇偶性条件选择不同的内部循环范围。

8.通电
【问题描述】2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为sqrt((x_1-x_2)*(x_1-x_2)+(y_1-y_2)*(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。在上式中 sqrt 表示取括号内的平方根。由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。
【输入格式】输入的第一行包含一个整数 n ,表示村庄的数量。接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。
【输出格式】输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。
【样例输入】4 \n 1 1 3 \n 9 9 7 \n 8 8 6 \n 4 5 4
【样例输出】17.41
【评测用例规模与约定】对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 100;
对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。

  • 标准答案
#include 
#include 
#include 
#include 

using namespace std;

struct Village {
    int x, y, h;
};

// 计算连接两个村庄所需的费用
double calculate(Village a, Village b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2)) + pow(a.h - b.h, 2);
}

// 计算通电的最低成本
double minCOST(vector villages) {
    int n = villages.size();
    vector cost(n, INFINITY);
    vector connected(n, false);
    cost[0] = 0; // 第一个村庄是可以建立发电站的地方
    for (int i = 0; i < n; i++) {
        int next = -1;
        double minCost = INFINITY;
        // 找到下一个要连接的村庄
        for (int j = 0; j < n; j++) {
            if (connected[j] == false && cost[j] < minCost) {
                minCost = cost[j];
                next = j;
            }
        }
        connected[next] = true;
        // 更新通过新连接的村庄到其他村庄的成本
        for (int j = 0; j < n; j++) {
            if (connected[j] == false) {
                double newCost = calculate(villages[next], villages[j]);
                if (newCost < cost[j]) {
                    cost[j] = newCost;
                }
            }
        }
    }
    double totalCost = 0;
    for (double c : cost) {
        totalCost += c;
    }
    return totalCost;
}

int main() {
    int n;
    cin >> n;
    vector villages(n);
    for (int i = 0; i < n; i++) {
        cin >> villages[i].x >> villages[i].y >> villages[i].h;
    }
    double result = minCOST(villages);
    cout << fixed << setprecision(2) << round(result * 100) / 100;
    return 0;
}

【思路解析】结构体定义一个名为Village的结构体,用于表示村庄的坐标和高度。calculate函数用于计算连接两个村庄之间所需的费用。minCOST函数是解决问题的核心。初始化两个vector,一个用于存储每个村庄到已连接村庄的最小费用,另一个vector用于标记村庄是否已经连接到电站。然后,从第一个村庄开始,逐步连接其他村庄以使总成本最小化。
【考察范围】① 贪婪算法:使用贪婪算法解决村庄供电问题,逐步连接村庄使总成本最小化。
② 数据结构:使用了vector来存储村庄信息和费用,以及结构体Village来表示村庄的属性。
③ 输出:使用setprecision函数来保留两位小数,使用round函数来四舍五入。

9.植树
【问题描述】小明和朋友们一起去郊外植树,他们带了一些在自己实验室精心研究出的小树苗。小明和朋友们一共有 n 个人,他们经过精心挑选,在一块空地上每个人挑选了一个适合植树的位置,总共 n 个。他们准备把自己带的树苗都植下去。然而,他们遇到了一个困难:有的树苗比较大,而有的位置挨太近,导致两棵树植下去后会撞在一起。他们将树看成一个圆,圆心在他们找的位置上。如果两棵树对应的圆相交,这两棵树就不适合同时植下(相切不受影响),称为两棵树冲突。小明和朋友们决定先合计合计,只将其中的一部分树植下去,保证没有互相冲突的树。他们同时希望这些树所能覆盖的面积和(圆面积和)最大。
【输入格式】输入的第一行包含一个整数 n ,表示人数,即准备植树的位置数。接下来 n 行,每行三个整数 x, y, r,表示一棵树在空地上的横、纵坐标和半径。
【输出格式】输出一行包含一个整数,表示在不冲突下可以植树的面积和。由于每棵树的面积都是圆周率的整数倍,请输出答案除以圆周率后的值(应当是一个整数)。
【样例输入】6 \n 1 1 2 \n 1 4 2 \n 1 7 2 \n 4 1 2 \n 4 4 2 \n 4 7 2
【样例输出】12
【评测用例规模与约定】对于 30% 的评测用例,1 <= n <= 10;
对于 60% 的评测用例,1 <= n <= 20;
对于所有评测用例,1 <= n <= 30,0 <= x, y <= 1000,1 <= r <= 1000。

  • 标准答案
#include 
#include 
#include 
#include 

using namespace std;

struct Tree {
    int x, y, r;
};

bool compareRadius(Tree tree1, Tree tree2) {
    return tree1.r > tree2.r; 
}

int main() {
    int n;
    cin >> n;
    vector trees(n);
    for (int i = 0; i < n; ++i) {
        cin >> trees[i].x >> trees[i].y >> trees[i].r;
    }
    // 按半径从大到小对树进行排序
    sort(trees.begin(), trees.end(), compareRadius);
    int result = 0; // 记录可以植树的总面积
    vector planted(n, false); // 记录每棵树是否已经植下
    for (int i = 0; i < n; i++) {
        if (planted[i] == false) {
            result += pow(trees[i].r, 2); // 植下半径最大的树
            planted[i] = true;
            for (int j = i + 1; j < n; j++) {
                // 检查是否与其他树冲突
                if (pow(trees[i].x - trees[j].x, 2) + pow(trees[i].y - trees[j].y, 2)
                    <= pow(trees[i].r + trees[j].r, 2) && planted[j] == false) {
                    planted[j] = true;
                }
            }
        }
    }
    cout << result;
    return 0;
}

【思路解析】定义一个结构体Tree,用于表示树的位置和半径。对树按照半径从大到小进行排序。接着循环遍历所有的树,计算其面积并累加。同时记录哪些树已经被植下,避免重复植树。在每次处理一个树时,会检查是否与后面的树发生冲突。如果两棵树的距离小于等于它们的半径之和,说明会相互干扰,因此只能选择其中一棵植树。
【考察范围】① 数据结构:使用了vector来存储树的信息,以及结构体Tree来表示树的属性。
② 排序算法:使用了sort函数对树进行排序,排序的依据是树的半径。
③ 循环和条件判断:使用了循环来遍历树,并进行条件判断以确定植树方案。

你可能感兴趣的:(刷题笔记 - October 2023)