【Level 0】数组(vector)
该比赛已结束,您无法在比赛模式下递交该题目。您可以点击“在题库中打开”以普通模式查看和递交本题。
一维vector
除非迫不得已,否则我们在教程题目中不讨论 C 语言的情形,专注于 C++ 的特性。
当我们需要存储大量数据时,例如 10000 个,声明这么多变量显然是不现实的,因此,对于同一种类型的数据,我们需要用一些数据结构进行存储。
我们从最常用的数组开始讨论,在 C++ 中我们应尽量采用 vector,既可以当作静态数组、也可以当作动态数组使用。(为节约时间,后续需要的引入头文件一般就是类型名字,如 #include <vector>,不再赘述)
首先声明一些一维的 vector
vector<int> vec;
vector<string> strings;
类型为 vector<int>,说明这是一个存放 int 类型的 vector,变量名为 vec。第二行同理,是一个存放 string 类型的 vector
push_back
如果需要在 尾部 添加元素,使用 push_back(x),其中 x 是新增的元素
int x = 5;
vec.push_back(x);
vec.push_back(3);
这样,原本为空的数组,就有了两个值:[5, 3]
数组可以直接由下标访问,例如
cout << vec[1] << " " << vec[0] << endl;
输出的结果是 3 5(注意,通常下标都是从0开始)
insert
如果要在某个位置插入元素,需要使用 insert(pos, elem),可能你需要在后面的 STL 章节才能完全理解,这里只需要了解使用方法即可。
vector<int> nums = {1, 2};
nums.insert(nums.begin() + 1, 3);
在位置 1 插入 3,执行后 nums[1] 变为 3,在原来位置 1 及在其之后的元素均往后挪一个位置。此时 nums 变为 1, 3, 2
size
size() 函数可以获取这个 vector 当前的大小,例如
vector<double> nums;
nums.push_back(1.0);
cout << nums.size() << " ";
nums.push_back(5.0);
nums.push_back(2.0);
cout << nums.size() << " ";
可以得到 1 3
当然,这样的动态添加适用于数据量未知的情况,实际上是比较慢的,因此,在数据量已知,或者数据量范围已知的情况下,我们可以直接申请一个指定大小的数组,这样效率较高,如
const int MAXN = 100000 + 5 // 可简单写成1e5 + 5
vector<int> vec(MAXN);
需要注意的是,在初始化时指定大小,内容是常量,如用 const 定义的常量 MAXN,或者直接写 100005。
你可以尝试定义一个变量如 int n = 5,那么此时用 vec(n) 声明时,相当于直接声明大小为 5,而对 n 进行修改,不影响 vec 的大小。
那么,我们是否有办法在运行过程中快速改变 vector 的大小呢?使用 resize(new_size) 即可
int n;
cin >> n;
vec.resize(n);
上述代码就将已经定义好的 vector,改变大小为从控制台输入的 n
简化遍历方法
与 C 语言最接近的方法,我们可以用下标来遍历 vector,如下所示
vector<int> nums;
...
for (int i = 0; i < nums.size(); ++i) {
// do something...
}
除了用下标遍历之外,如果我们想要遍历 vector 中所有元素,还可以用下面的形式:
vector<int> nums;
...
for (int x : nums) {
// do something...
}
如果再用之后介绍的 auto,则会更加方便,可以自动推导元素的类型,不论 vector 元素是什么类型,都只需要一个 auto 即可。
vector<double> nums;
...
for (auto x : nums) {
// do something...
}
二维vector
在 vector 中存放的类型几乎是任意的,除了常用类型如 vector<int>, vector<double>, vector<string> 等之外,我们也可以在 vector 中存放 vector,例如
vector<vector<int>> nums;
注意到 nums 这个 vector,每个位置所存放的元素的类型是 vector<int>。换言之,用 nums[i] 取出的内容,是一个 vector<int>,例如(我们可以通过如下所示的操作,用花括号初始化 vector 的内容)
vector<int> vec1 = {1, 2, 3, 4, 5};
vector<int> vec2 = {6, 7, 8, 9, 10, 11};
nums.push_back(vec1);
nums.push_back(vec2);
特别注意:将 vec1 和 vec2 放进 nums 后,vec1、vec2 与 nums 无关,即改变 vec1 内的值,并不能改变 nums 内的值
这样一来,原本为空的 nums 就有了两个 vector<int>,我们可以先用 nums[i] 取出第 i 个 vector<int>,再对其进行操作,例如:
cout << nums[1].size() << " " << nums[0].size() << endl;
我们分别取出第 1 个和第 0 个 vector,并输出它们的大小,结果是 6 5
既然我们取出的内容是一个 vector<int>,那么我们就可以对它进行一维 vector的所有操作,如
for (int i = 0; i < 3; i++) {
cout << nums[1][i] << " ";
}
用 nums[1] 取出第 1 个 vector<int>,即可通过下标访问其中的元素,例如此处访问下标为 0-2 的 3 个元素,输出结果是 6 7 8
快速初始化
对于 nums 这样的二维 vector,我们在大小已知时,动态添加一维 vector 依然耗时,例如对于给定的 n*m 矩阵,需要一种初始化方法快速地构建一个二维 vector。
一维 vector 初始化时,我们只给定了它的大小,事实上此时初值默认为 0,我们也可以在初始化时同时指定初值,例如
vector<int> vec(MAXN, 1);
这样就使得 vec 里的值全部为 1。自然,我们也可以用同样的方法来初始化一个二维的 vector,例如
vector<vector<int>> nums(M, vector<int>(N));
可以看到,nums 的大小为 M,含有 M 个大小为 N 的一维 vector。
总之,我们不要把二维 vector 当作传统的二维数组来看待,只需要看成一个 vector,只不过这个 vector 里面所装的元素是一个个 vector 而已。因此只要是 vector 的操作,我们都可以运用在 nums 上。
Description
对于给定的矩阵 ,分别求出矩阵的 1 范数 与矩阵的无穷范数
Format
Input
第一行为两个整数 ,表示矩阵的行数与列数
接下来 行,每行有 个 浮点数,表示矩阵的值
Output
两个数,分别表示矩阵的 1 范数和无穷范数
Samples
2 2
1.5 2.7
3.4 9.8
12.5 13.2
3 4
2 3 -5 -7
4 6 8 -4
6 -11 -3 16
27 36