CSP-202006-2-稀疏向量
关键点:时间复杂度
1.暴力枚举(30分)
- 在传统的暴力枚举方法中,我们有两个长度为 a 和 b 的稀疏向量,需要对这两个向量的全部元素逐一比较,如果 index 相等则进行乘积计算,否则不进行计算,在这种情况下,时间复杂度是 O ( n 2 ) O(n^2) O(n2),对于大量数据来讲时间开销过大。
2.优化方法(100分)
-
同步遍历两个向量:通过同时遍历两个稀疏向量的非零元素,我们可以仅在索引相同的情况下计算乘积,并累加这些乘积。这避免了对不会对最终结果产生影响的元素(即其中一个向量在该索引处为零的元素)进行计算。
-
按索引顺序比较和移动:通过比较当前遍历到的两个元素的索引,我们可以决定移动哪个向量的遍历指针。如果一个向量的当前元素索引小于另一个向量的,那么我们只需移动索引较小的那个向量的指针,因为只有当两个向量的索引相同时,元素的乘积才会对最终的点积结果有贡献。这样的比较和移动确保了我们只在必要时才进行计算,进一步降低了时间复杂度。
-
时间复杂度:在最坏的情况下,每当我们比较两个向量中的一个元素时,我们可能需要移动另一个向量的指针(但通常不会同时移动两个指针,除非两个元素索引相同)。因此,最坏情况下的时间复杂度是 O ( a + b ) O(a + b) O(a+b),因为每个非零元素最多被比较和移动一次。这是一个显著优于 O ( n 2 ) O(n^2) O(n2) 的改进,特别是当 a 和 b 都远小于 n 时。
解题思路
-
结构体
MyPoint
,用于存储向量的非零元素的索引和值。 -
通过输入获取向量的维度
n
(这个值在后续的计算中没有直接使用)以及两个稀疏向量u
和v
的非零元素的数量a
和b
。 -
接下来,通过循环读取这些非零元素的索引和值,并将它们存储到向量
u
和v
中。 -
在计算点积的部分,初始化两个指针
ui
和vi
(分别指向u
和v
的当前元素),以及用于存储最终点积结果的变量aws
。 -
进入一个循环,在没有遍历完两个向量的情况下,不断进行以下操作:
- 比较当前元素的索引: 比较当前处理的两个向量的非零元素的索引来决定下一步的操作。由于向量
u
和v
中的非零元素是按索引顺序存储的,这允许我们通过顺序访问来比较它们的索引。 - 移动指针: 如果
u
的当前元素的索引小于v
的当前元素的索引,这意味着u
的这个非零元素在v
中对应的位置是0(因为在v
中没有这个索引的非零元素)。因此,我们不需要计算这两个元素的乘积,而是需要考虑u
的下一个非零元素,所以移动u
的指针;相反,如果u
的当前元素的索引大于v
的当前元素的索引,这意味着v
的这个非零元素在u
中对应的位置是0。所以我们移动v
的指针。 - 计算乘积并累加: 如果两个向量的当前元素具有相同的索引,这意味着我们找到了两个向量中都非零的对应元素,这时我们就计算这两个非零元素的乘积,并将乘积累加到总和
aws
中。然后,我们同时移动u
和v
的指针来考虑各自的下一个非零元素。
- 比较当前元素的索引: 比较当前处理的两个向量的非零元素的索引来决定下一步的操作。由于向量
-
当任一个向量遍历完毕(即
ui
等于a
或vi
等于b
),循环结束。
完整代码
#include <iostream>
#include <vector>
using namespace std;struct MyPoint
{int index, num;
};
long long n, a, b, myIndex, myValue, aws, ui, vi;
vector<MyPoint>u, v;int main()
{cin >> n >> a >> b;for (int i = 0; i < a; i++){cin >> myIndex >> myValue;u.push_back({ myIndex,myValue });}for (int i = 0; i < b; i++){cin >> myIndex >> myValue;v.push_back({ myIndex,myValue });}while (true){if (u[ui].index < v[vi].index) ui++;else if (u[ui].index > v[vi].index) vi++;else {aws += u[ui].num * v[vi].num;ui++, vi++;}if (ui == a || vi == b)break;}cout << aws;return 0;
}