机试旧题新讲一

最近在准备学校复试,刷一些老题旧题,在N诺oj和牛客网上都有提交。今天就总结部分题目,只写我觉得值得写的(当然还有一些我不会写,不想去学了)
1、二进制数

输入输出格式
输入描述:
每行有一个数字n(0<=n<=10^8),表示要求的二进制串。
输出描述:
输出共T行。每行输出求得的二进制串。
输入输出样例
输入样例#:
23
输出样例#:
10111

reverse函数必须使用首尾指针,可以反转vector数组也可以反转string字符串。

#include<bits/stdc++.h>
using namespace std;
int main(){unsigned int n;cin>>n;vector<int> res;while (n != 0) {int tmp = n%2;n = n/2;res.push_back(tmp);}reverse(res.begin(),res.end());for (auto i : res) {cout<<i;}
}

2、查找第K小数

查找一个数组的第K小的数,注意同样大小算一样大。 如 2 1 3 4 5 2 第三小数为3。

输入输出格式
输入描述:
输入有多组数据。
每组输入n,然后输入n个整数(1<=n<=1000),再输入k。
输出描述:
输出第k小的整数。
输入输出样例
输入样例#:
6
2 1 3 5 2 2
3
输出样例#:
3

因为要找第k小不同的数,因此不能使用堆来写,因为必须经过一次排序,时间复杂度最快O(nlogn),然后对比前后不相同的第几个值。

#include<bits/stdc++.h>
using namespace std;
int main () {int n;while (cin>>n) {vector<int> record(n);for (int i = 0; i < n; i++) {cin>>record[i];}sort(record.begin(), record.end());int k;cin>>k;int count = 1;for (int i = 1;i < n; i++) {if (record[i] != record[i-1]){count++;if (count == k) {cout<<record[i]<<endl;}}}}return 0;
}	

3、查找1

输入数组长度 n
输入数组 a[1…n]
输入查找个数m
输入查找数字b[1…m]
输出 YES or NO 查找有则YES 否则NO 。
输入输出格式
输入描述:
输入有多组数据。
每组输入n,然后输入n个整数,再输入m,然后再输入m个整数(1<=m<=n<=100)。
输出描述:
如果在n个数组中输出YES否则输出NO。
输入输出样例
输入样例#:
6
3 2 5 4 7 8
2
3 6
输出样例#:
YES
NO

哈希表法,直接定为每个元素是否出现过。

#include<bits/stdc++.h>
using namespace std;
int main() {int n, m;while (cin>>n) {vector<int> nums1(n);vector<int> record(10000, 0);for (int i = 0; i < n; i++) {cin>>nums1[i];record[nums1[i]] = 1;}cin>>m;vector<int> nums2(m);for (int i = 0; i < m; i++) {cin>>nums2[i];if (record[nums2[i]] == 1)cout<<"YES"<<endl;elsecout<<"NO"<<endl;}return 0;}
}

4、哈夫曼树

哈夫曼树,第一行输入一个数n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出所有结点的值与权值的乘积之和。
输入输出格式
输入描述:
输入有多组数据。
每组第一行输入一个数n,接着输入n个叶节点(叶节点权值不超过100,2<=n<=1000)。
输出描述:
输出权值。
输入输出样例
输入样例#:
5
1 2 2 5 9
输出样例#:
37

这里用到了优先队列priority_queue,再强调一遍,优先队列默认是less比较仿函数,大顶堆(与sort排序相反)。因此如果需要小顶堆(求最大的k个值,出现最多次的k个数),要在优先队列的第三个参数使用greater<int>。求WPL。

#include<bits/stdc++.h>
using namespace std;
int main() {int n;while (cin>>n) {vector<int> nums(n);for (int i = 0; i < n; i++) {cin>>nums[i];}priority_queue<int ,vector<int>, greater<int>> q;for (int i = 0; i < n; i++) {q.push(nums[i]);}int res = 0;while (q.size()>1) {int tmp1 = q.top();q.pop();int tmp2 = q.top();q.pop();int sum = tmp1 + tmp2;res += sum;q.push(sum);}cout<<res<<endl;}
}		

5、查找 - 北邮

读入一组字符串(待操作的),再读入一个int n记录记下来有几条命令,总共有2中命令:1、翻转 从下标为i的字符开始到i+len-1之间的字符串倒序;2、替换 命中如果第一位为1,用命令的第四位开始到最后的字符串替换原读入的字符串下标 i 到 i+len-1的字符串。每次执行一条命令后新的字符串代替旧的字符串(即下一条命令在作用在得到的新字符串上)。 命令格式:第一位0代表翻转,1代表替换;第二位代表待操作的字符串的起始下标int i;第三位表示需要操作的字符串长度int len。
输入输出格式
输入描述:
输入有多组数据。
每组输入一个字符串(不大于100)然后输入n,再输入n条指令(指令一定有效)。
输出描述:
根据指令对字符串操作后输出结果。
输入输出样例
输入样例#:
bac
2
003
112as
输出样例#:
cab
cas

这里涉及到string的substr、erase、insert函数,需要指出三个函数都可以采用角标(非迭代器)定位首地址,所有左右区间的都是左闭右开,insert函数插入的必须是string类型,不能是char类型。swap函数,老朋友了,交换string,vector都可以。

#include<bits/stdc++.h>
using namespace std;
void reverse(string &s, int begin, int end) {for (int i = begin, j = end; i < j; i++,j--) {swap(s[i],s[j]);}
}
int main() {string str;while (cin>>str) {int n;cin>>n;for (int i = 0; i < n; i++) {string s;cin>>s;//反转if (s[0] == '0') {int begin = s[1]-'0';int end = s[2]-'0'+begin-1;reverse(str, begin, end);cout<<str<<endl;}else if (s[0] == '1') {int begin = s[1]-'0';int end = s[2]-'0'+begin-1;string s1 = s.substr(3, s.size());str.erase(str.begin()+begin, str.begin()+end+1);str.insert(begin,s1);cout<<str<<endl;}}}return 0;
}		

6、二叉树(北京邮电大学)

输入二叉树的前序遍历和中序遍历结果,输入二叉树的后序遍历结果。
输入输出格式
输入描述:
第一行为二叉树先序遍历结果。
第二行为二叉树中序遍历结果。
输出描述:
二叉树后续遍历结果
输入输出样例
输入样例#:
426315
623415
输出样例#:
632514

构造二叉树,太难辣!这次是前序加中序构建二叉树,那么下次万一是后序加中序构建二叉树呢!于是勤奋的我把两种都给出来了!然后注意定义树怎么定义:

struct TreeNode{int val;TreeNode *left;TreeNode *right;//构造结构TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
//前、中序构建二叉树
#include<bits/stdc++.h>
using namespace std;
struct TreeNode{int val;TreeNode *left;TreeNode *right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* traversal(vector<int>& preorder, vector<int>& inorder)
{if(preorder.size() == 0)return nullptr;int rootValue = preorder[0];TreeNode* root = new TreeNode(rootValue);// 确定中序分割点int Index;for(Index = 0; Index < inorder.size(); Index++){if(inorder[Index] == rootValue) break;}// 分割中序vector<int> leftInorder(inorder.begin(), inorder.begin() + Index);vector<int> rightInorder(inorder.begin() + Index + 1, inorder.end());// 分割前序preorder.erase(preorder.begin());vector<int> leftPreorder(preorder.begin(), preorder.begin() + leftInorder.size());vector<int> rightPreorder(preorder.begin() + leftInorder.size(), preorder.end());// 开始构建树root->left = traversal(leftPreorder, leftInorder);root->right = traversal(rightPreorder, rightInorder);return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {if(preorder.size() == 0 || inorder.size() == 0)return nullptr;return traversal(preorder, inorder);
}
void postTree(TreeNode *t) {if (t != nullptr) {postTree(t->left);postTree(t->right);cout<<t->val;}
}
int main() {vector<int> preTree;vector<int> inTree;string s1,s2;cin>>s1>>s2;for (int i = 0; i < s1.size(); i++) {preTree.push_back(s1[i] - '0');}for (int i = 0; i < s2.size(); i++) {inTree.push_back(s2[i] - '0');}TreeNode *t = buildTree(preTree, inTree);postTree(t);return 0;
}

resize函数可以把后面的去掉。

//中序、后序构造二叉树
class Solution {
public:TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){// 空节点返回nullif(postorder.size() == 0) return nullptr;int rootValue = postorder[postorder.size()-1];//后序遍历最后一个数// 新建的一个节点TreeNode* root = new TreeNode(rootValue);// 中序分割int index;for(index = 0; index < inorder.size(); index++){if(inorder[index] == rootValue)break;}vector<int> leftInorder(inorder.begin(), inorder.begin() + index);vector<int> rightInorder(inorder.begin() + index + 1,inorder.end());// 后序分割postorder.resize(postorder.size() - 1);vector<int> leftPostorder(postorder.begin(),postorder.begin() + index);vector<int> rightPostorder(postorder.begin() + index, postorder.end());// 接上子节点root->left = traversal(leftInorder, leftPostorder);root->right = traversal(rightInorder, rightPostorder);return root;}TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {// 空树if(inorder.size() == 0 || postorder.size() == 0) return nullptr;return traversal(inorder, postorder);}
};

7、二进制数字翻转

入数据组数t

每组数据输入一个十进制数x(0<x<2^32),将其二进制位反转(共32位),然后输出对应的十进制数
输入输出格式
输入描述:
如题
输出描述:
如题
输入输出样例
输入样例#:
2
2
3
输出样例#:
1073741824
3221225472
提示
2^32-1超出了int的范围,请使用long long

他真的,我哭死,他居然提醒我用long long,好贴心啊!

#include<bits/stdc++.h>
using namespace std;
int main() {int t;cin>>t;while(t--) {long long n;cin>>n;vector<int> Binary(32, 0);for (int i = 0; i <32; i++) {Binary[i] = n%2;n /= 2;if (n == 0)break;}//reverse(Binary.begin(), Binary.end());long long sum = 0;for (int i = 0; i < 32; i++){sum *= 2;sum += Binary[i];}cout<<sum<<endl;}return 0;
}

8、发财数

一个大于等于2的整数,如果可以分解为8个或8个以上的素数相乘,则称其为发财数,让你输出第n个发财数
输入输出格式
输入描述:
输入一个整数t,代表有t组数据(t<1000)
输入一个正整数n,(n<=10000)
输出描述:
输出第n个发财数。
输入输出样例
输入样例#:
1
1
输出样例#:
256

枚举遍历,每一次都除以一个数,如果可以除尽。接着除,否则除的数加一,往后看,看是否可以被八个数整除。

#include<bits/stdc++.h>
using namespace std;
int main () {int t;cin>>t;vector<int> nums;while (t--) {int n;cin>>n;
//		if (n <= nums.size()) {
//			cout<<nums[n-1]<<endl;
//		}for (int i = (nums.size() > 0 ? nums.back() + 1 : 256); nums.size() <= n+1; i++) {int value = i;int count = 8;for (int j = 2; j*j <= value;) {if (value % j == 0) {count--;value /= j;if (count == 1) {nums.push_back(i);}}else {j++;}}}
//		for (int i = 0; i < nums.size();i++) {
//			cout<<nums[i]<<" ";
//		}cout<<nums[n-1]<<endl;}return 0;
}					

9、复数集合

一个复数(x+iy)集合,两种操作作用在该集合上: 1、Pop 表示读出集合中复数模值最大的那个复数,如集合为空 输出 empty ,不为空就输出最大的那个复数并且从集合中删除那个复数,再输出集合的大小SIZE; 2 Insert a+ib 指令(a,b表示实部和虚部),将a+ib加入到集合中 ,输出集合的大小SIZE; 最开始要读入一个int n,表示接下来的n行每一行都是一条命令。
输入输出格式
输入描述:
输入有多组数据。
每组输入一个n(1<=n<=1000),然后再输入n条指令。
输出描述:
根据指令输出结果。
模相等的输出b较小的复数。
a和b都是非负数。
输入输出样例
输入样例#:
3
Pop
Insert 1+i2
Pop
输出样例#:
empty
SIZE = 1
1+i2
SIZE = 0

排序固然可,但是堆更妙。在结构体里构建排序函数,重载运算符<号可以自定义堆的排序方式。这样出来的大顶堆是按照我们自己定义的方式排序的大顶堆。还有一件事,就是出入如果不符合,那么就会卡住,比如你出入一个字符,但是我用int来接,就接不了,会卡在缓冲区,因此如果有一个字符你不要可以用一个临时变量将其取出,然后不作处理。还有一件事,就是比如234+345这样的式子,可以用int,char,int分别取出三个部分这个真的有用哎!如果一串1 2 3 5,如果想当做字符串,可以getline(cin,str)。

#include<bits/stdc++.h>
using namespace std;
struct Complex{int x;int y;bool operator< (Complex node) const{if (x*x+y*y == node.x*node.x+node.y*node.y) {return y > node.y;}return x*x+y*y < node.x*node.x+node.y*node.y;}
};int main() {int n;while (cin>>n) {string cmd;priority_queue<Complex> q;while (n--) {cin>>cmd;Complex C;if (cmd == "Pop") {if (q.size() == 0) {cout<<"empty"<<endl;}else {cout<<q.top().x<<"+i"<<q.top().y<<endl;q.pop();cout<<"SIZE = "<<q.size()<<endl;}}else if (cmd == "Insert") {char o,c;cin>>C.x>>o>>c>>C.y;q.push(C);cout<<"SIZE = "<<q.size()<<endl;}}}return 0;
}

10、矩阵幂

给定一个n*n的矩阵,求该矩阵的k次幂,即P^k。
输入输出格式
输入描述:
第一行:两个整数n(2<=n<=10)、k(1<=k<=5),两个数字之间用一个空格隔开,含义如上所示。
接下来有n行,每行n个正整数,其中,第i行第j个整数表示矩阵中第i行第j列的矩阵元素Pij且(0<=Pij<=10)。另外,数据保证最后结果不会超过10^8。
输出描述:
对于每组测试数据,输出其结果。格式为:
n行n列个整数,每行数之间用空格隔开,注意,每行最后一个数后面不应该有多余的空格。
输入输出样例
输入样例#:
2 2
9 8
9 3
输出样例#:
153 96
108 81

其实不用快速幂也可以完成,但是可以用更dio的方法为什么不用呢对吧!快速幂,类似求01串转化为十进制数,从低位开始,结果值初始化为1,如果低位为零,则不作处理,低位自己平方;否则结果值乘前面说的低位平方(一直累计)。本题是模板题,要背!
咱就是自己归纳三部曲:
1、重载运算符
,返回值为二维数组,其实就是将普通乘法变为矩阵乘法。矩阵乘矩阵返回矩阵。
2、初始化结果矩阵,结果矩阵必须初始化为单位阵。
3、快速幂方法:
*

//快速幂方法while (k>0) {if (k % 2 == 1) {Q = Q * P;}P = P * P;k /= 2;}
#include<bits/stdc++.h>
using namespace std;
//重载运算符*变为矩阵乘法,返回一个矩阵
vector<vector<int>> operator*(const vector<vector<int>> &a,const vector<vector<int>> &b) {int n = a.size();vector<vector<int>> tmp(n, vector<int>(n, 0));for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {for (int k = 0; k < n; k++) {tmp[i][j] += a[i][k] * b[k][j];}}}return tmp;
}int main () {int n,k;while (cin>>n>>k) {	vector<vector<int>> P(n, vector<int>(n, 0));vector<vector<int>> Q(n,vector<int>(n, 0));//结果矩阵对角线为1for (int i = 0; i < n; i++) {Q[i][i] = 1;}for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {cin>>P[i][j];}}//快速幂方法while (k>0) {if (k % 2 == 1) {Q = Q * P;}P = P * P;k /= 2;}for (int i = 0; i < n; i++) {cout<<Q[i][0];for (int j = 1; j < n; j++) {cout<<" "<<Q[i][j];}cout<<endl;}}
}

今天就先总结十道题吧,如果明天状态好,总结剩下的题。其他都写了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

探索顺序表操作:从理论到实践

### 探索顺序表操作&#xff1a;从理论到实践 顺序表是数据结构中一种基础而重要的线性表&#xff0c;它通过一段连续的存储空间顺序存储数据元素。与链表相比&#xff0c;顺序表的优势在于支持随机访问&#xff0c;使得数据的检索效率非常高。然而&#xff0c;由于数据存储的…

寄快递很麻烦怎么办?无脑方法教会你便宜寄快递!快冲!

现在我们每天都会去寄快递&#xff0c;不仅寄大件还会发物流&#xff0c;但是我们真的了解快递的价格吗&#xff1f;寄快递必须拿到快递驿站吗&#xff1f;去菜鸟驿站寄快递会给我们便宜吗&#xff1f;有没有什么便宜的寄快递的方法呢&#xff1f;驿站会有包装快递的包装袋吗&a…

Wireshare捕获接口中没有本地连接

1. 查看npf服务是否启动 服务名无效&#xff0c;需要安转WinPcap 2. 勾选Npcap Packet Driver (NPCAP) 3. 重新启动Wireshark 重新启动Wireshark后&#xff0c;本地连接有了

Linux下使用Pycharm

1.下载pycharm 访问Pycharm官网地址https://www.jetbrains.com/pycharm/download/#sectionlinux&#xff0c;找到PyCharm的下载页面。接着&#xff0c;根据个人需求&#xff0c;选择适宜的操作系统及版本&#xff0c;下载官方提供的pycharm.tar.gz文件进行安装&#xff0c;安装…

SpringCloud从入门到精通速成(一)

文章目录 1.认识微服务1.0.学习目标1.1.单体架构1.2.分布式架构1.3.微服务1.4.SpringCloud1.5.总结 2.服务拆分和远程调用2.1.服务拆分原则2.2.服务拆分示例2.2.1.导入Sql语句2.2.2.导入demo工程 2.3.实现远程调用案例2.3.1.案例需求&#xff1a;2.3.2.注册RestTemplate2.3.3.实…

学生信息管理系统--修改信息(非常详细的修改,更新,撤销,删除逻辑)

目录 概述修改包括的操作修改在每个模块中的应用 详解修改与更新取消删除 特殊概念数据集游标 总结 概述 学生信息管理系统&#xff0c;功能相对简单且代码重复性高&#xff0c;应该采用复用的思想来减少代码的冗余和提高代码的可维护性。然而&#xff0c;对于基础入门项目来说…

NVM使用教程

文章目录 ⭐️写在前面的话⭐️1、卸载已经安装的node2、卸载nvm3、安装nvm4、配置路径以及下载源5、使用nvm下载node6、nvm常用命令7、全局安装npm、cnpm8、使用淘宝镜像cnpm9、配置全局的node仓库&#x1f680; 先看后赞&#xff0c;养成习惯&#xff01;&#x1f680;&#…

Word2vec学习笔记

&#xff08;1&#xff09;NNLM模型&#xff08;神经网络语言模型&#xff09; 语言模型是一个单纯的、统一的、抽象的形式系统&#xff0c;语言客观事实经过语言模型的描述&#xff0c;比较适合于电子计算机进行自动处理&#xff0c;因而语言模型对于自然语言的信息处理具有重…

MySQL学习八:窗口函数(一)

目录 一、窗口函数1. 窗口函数定义2. 窗口函数语法3. 演示表格一4. 窗口的确定4.1 例1&#xff1a;查询各班级总分4.2 例2&#xff1a;查询各班级累计总分4.3 分区子句&#xff08;partition by&#xff09;4.4 排序子句&#xff08;order by&#xff09;4.5 窗口子句&#xff…

Linux系统及操作 (08)

Linux系统及操作 (07) 逻辑卷管理&#xff08;LVM&#xff09; 逻辑卷 Logical Volume Manager&#xff1a; 整合分散的空间空间支持扩大/dev/mapper/* 支持动态升级&#xff08;在线升级&#xff09;&#xff0c;不停机维护。 逻辑卷制作过程&#xff1a;众多的物理卷**(PV…

Resilience4j原理及应用:构建高可用系统(二)

本系列文章简介&#xff1a; 在本系列文章中&#xff0c;我们将深入探讨Resilience4j的原理及应用&#xff0c;包括熔断器、限流器和容错策略的工作原理、配置方法以及最佳实践。通过学习和掌握Resilience4j的使用技巧&#xff0c;我们将能够更好地构建高可用性系统&#xff0c…

【原创】5分钟拿下Floyd算法

文章目录 问题描述算法流程5分钟拿下 问题描述 一天小明捧着一本世界地图在看&#xff0c;突然小明拿起笔&#xff0c;将他最爱的那些城市标记出来&#xff0c;并且随机的将这些城市中的某些用线段两两连接起来。 小明量出了每条线段的长度&#xff0c;现在小明想知道在这些线段…

单片机-- 数电(3)

编码器与译码器 译码 &#xff1a;将二进制代码转化为其他进制的代码 编码 &#xff1a;就是将其他代码转换为二进制码 编码器的类型 1二进制编码器 用n位二进制数码对2的n次方个输入信号进行编码的电路 2二-十进制编码器 将0到9十个十进制数转化为二进制代码的电路 2…

crossover虚拟机 crossover软件干嘛的 虚拟机软件的使用方法 mac虚拟机装windows

与传统的虚拟机软件&#xff08;如VMware、VirtualBox&#xff09;相比&#xff0c;CrossOver具有更高的运行效率和更好的用户体验。因为它并不创建一个完整的Windows虚拟机&#xff0c;而是仅模拟应用程序所需的运行环境。这使得CrossOver在启动和运行Windows应用程序时更加快…

手撕HashMap底层源码(学习内容全)

day28上 集合框架 标绿已经学习底层&#xff0c;深入底层主要是研究实现类底层 手撕HashMap底层源码 JDK1.7版本的HashMap为例&#xff08;注意实验代码时进行版本切换&#xff09; 代码注释参考理解 //day27初识 public class HashMap<K,V> extends AbstractMap<K,…

洛谷 P1246编码

编码 题目描述 编码工作常被运用于密文或压缩传输。这里我们用一种最简单的编码方式进行编码&#xff1a;把一些有规律的单词编成数字。 字母表中共有 26 26 26 个字母 a , b , c , ⋯ , z \mathtt{a,b,c,\cdots,z} a,b,c,⋯,z&#xff0c;这些特殊的单词长度不超过 6 6 …

SpringBoot3整合Mybatis-Plus与PageHelper包冲突解决

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; SpringBoot3整合Mybatis-Plus与PageHelper包冲突解决 ⏱️ 创作时间&a…

Elasticsearch - Docker安装Elasticsearch8.12.2

前言 最近在学习 ES&#xff0c;所以需要在服务器上装一个单节点的 ES 服务器环境&#xff1a;centos 7.9 安装 下载镜像 目前最新版本是 8.12.2 docker pull docker.elastic.co/elasticsearch/elasticsearch:8.12.2创建配置 新增配置文件 elasticsearch.yml http.host…

大模型时代,微软AI投资的布局

这些领域涉及 3D、代码、销售、游戏等多个行业。其中&#xff1a; 在 3D 领域&#xff0c;blackshark.ai 利用 AI 技术提供地理空间数据解决方案&#xff1b;humane 专注于人机交互技术创新&#xff1b;Builder.ai 提供了无需编程知识的应用构建平台。代码方面&#xff0c;GitH…

C++ 编程入门指南:深入了解 C++ 语言及其应用领域

C 简介 什么是 C&#xff1f; C 是一种跨平台的编程语言&#xff0c;可用于创建高性能应用程序。 C 是由 Bjarne Stroustrup 开发的&#xff0c;作为 C 语言的扩展。 C 为程序员提供了对系统资源和内存的高级控制。 该语言在 2011 年、2014 年、2017 年和 2020 年进行了 4…