两个'rare'矩阵的总和,读取为坐标 X、坐标 Y 和一个值

Sum of two 'rare' matrices, read as coordinate X, coordinate Y and a value

本文关键字:坐标 一个 读取 rare 两个      更新时间:2023-10-16

当我在 pbinfo.ro 上遇到structs问题时,我发现了一个问题,其中我被赋予了两个矩阵的维度,每个矩阵的nonzero元素的数量(分别为N1,分别为N2),然后是N1 + N2组的3个数字,如下所示:

  • 坐标 X 和 Y
  • 位于这些坐标处的值

我所要做的就是编写一个程序来执行这两个矩阵的总和(它们都具有相同的维度)并输出输入中给定的总和(X、Y 和坐标处的值)。

以下是问题的全文:

矩阵-拉拉 | www.pbinfo.ro

要求

给出了两个稀有矩阵,你 被要求计算他们的总和。

矩阵A (n, m)如果其大多数元素等于 零(至少一半)。由于非零数的数量很少,因此 具有k非零元素的稀有矩阵A (n, m)可以使用 数组X包含k形式(行、列、值)的三元组,对应于 矩阵的非零值。数组X的元素存储在 按linecolumn排列的词典顺序 .

例如,矩阵n = m = 3

1 0 2
0 0 5
0 2 0

将以X方式保存:{(1,1,1), (1,3,2), (2,3,5), (3,2,2)}.

输入数据

输入文件matrice_rara.in第一行包含 两个矩阵的维度n m,表示行数 和列,以及N1 N2,矩阵的非零元素的数量A和 矩阵B.然后以下N1行将包含 矩阵按字典顺序A,最后N2行将包含 表示矩阵B的非零元素的三元组,也在 词典顺序。

输出数据

输出文件matrice_rara.out将在第一行包含 矩阵中的非零元素数C然后是矩阵 本身以字典顺序的三胞胎形式出现,每行一个。

限制和澄清

  • 1 ≤ n, m ≤ 1,000,000
  • 1 ≤ N1, N2 ≤ 300,000
  • -1,000,000,000 ≤ A[i][j], B[i][j] ≤ 1,000,000,000
  • Time Limit: 1 second
  • Memory Limit: 64 MB / 8 MB

我尝试的是我读取一个三元组数组中的所有三元组,对数组进行排序,然后将具有相同坐标的元素的值添加到具有重复坐标的数组中的第一个元素中。

这是我的代码:

#include <fstream>
#include <algorithm>
using namespace std;
ifstream cin("matrice_rara.in");
ofstream cout("matrice_rara.out");
struct matrix
{
int coord1, coord2;
long long val;
} data[600001];
bool operator<(matrix const &a, matrix const &b)
{
if (a.coord1 == b.coord1) {
if (a.coord2 == b.coord2) {
return a.val < b.val;
}
return a.coord2 < b.coord2;
}
return a.coord1 < b.coord1;
}
int main()
{
int lin, col, unNul1, unNul2;
cin >> lin >> col >> unNul1 >> unNul2;
for (int i = 1; i <= unNul1 + unNul2; i++)
cin >> data[i].coord1 >> data[i].coord2 >> data[i].val;
sort(data + 1, data + unNul1 + unNul2 + 1);
int unNull = 0;
for (int i = 1; i <= unNul1 + unNul2; i++)
{
int start = i;
while (data[i + 1].coord1 == data[i].coord1 && data[i + 1].coord2 == data[i].coord2 && i + 1 <= unNul1 + unNul2)
i++, data[start].val += data[i].val;
if (data[start].val)
unNull++;
}
cout << unNull << "n";
for (int i = 1; i <= unNul1 + unNul2; i++)
{
if (data[i].val)
cout << data[i].coord1 << " " << data[i].coord2 << " " << data[i].val << "n";
while (data[i + 1].coord1 == data[i].coord1 && data[i + 1].coord2 == data[i].coord2 && i + 1 <= unNul1 + unNul2)
i++;
}
}

上面的代码在 100 分中获得 65 分,在 9 个测试中得到 6 个测试的正确答案,其余测试得到错误答案(没有 TLE)。

有什么帮助吗?

如果您有逻辑 AND 运算符&&,并且左侧表达式false则不计算右侧表达式。这称为短路评估

这也意味着必须先评估左侧,然后代码才能知道是否需要评估右侧。

现在让我们从代码中获取一个条件表达式,并稍微简化一下:

data[i + 1].coord1 == data[i].coord1 && i + 1 <= unNul1 + unNul2

这是与&&运算符组合的两个子条件:

  1. 左侧data[i + 1].coord1 == data[i].coord1,以及
  2. 右侧i + 1 <= unNul1 + unNul2

由于&&算子的短路性质,必须先评估左侧(data[i + 1].coord1 == data[i].coord1),然后再评估右侧(i + 1 <= unNul1 + unNul2)。

现在,如果i恰好等于unNul1 + unNul2,则左侧表达式将使用超出 1 的索引。

为了不越界,您需要检查索引i,最简单的方法是将条件的顺序切换为

i + 1 <= unNul1 + unNul2 && data[i + 1].coord1 == data[i].coord1

现在i + 1不会越界。


至于为什么它很重要,这是因为未初始化的数据将具有不确定的值,并且以几乎所有方式使用它们都会导致未定义的行为。使用当前具有的循环条件,您将使用索引进入数组data未初始化的部分。

如果我理解了发布代码的意图,OP 试图将这个算法用于对两个稀疏矩阵求和:

  • 两个矩阵的非零元素读入一个足够大的数组中,以存储问题约束允许的最大元素数。请注意,输入格式良好,表示两个矩阵的 n.z. 元素的三元组已经按字典顺序排序(行主
  • )了。
  • 按字典顺序对整个数组进行排序。请注意,在比较函数中,n.z. 元素的值也被考虑在内。
  • 遍历数组并对两个连续元素的值求和(如果它们具有相同的坐标),并将结果存储在第一个元素中。如果结果值不为零,则相应地更新 n.z. 的计数。这里已经指出了越界访问。
  • 再次遍历数组以打印出结果矩阵的非零值,跳过具有相同坐标的连续元素的值。与以前一样,越界访问。

除了由于访问越界而导致的 UB 之外,我不清楚接受的解决方案是否需要实际检查每个元素的结果值是否为非零。

即使是这种情况,我认为我们也可以利用已经排序的输入,并通过"合并"两个源来增量创建结果矩阵。在下面的代码片段(可在此处测试)中,我还通过使用表示位置的单个 64 位值而不是两个 32 位坐标来简化两个元素之间的比较。

#include <iostream>
#include <vector>
#include <cstdint>
#include <cassert>
#include <utility>
#include <iterator>
constexpr uint64_t pos_from(uint32_t r, uint32_t c)
{
return (static_cast<uint64_t>(r) << 32) ^ c;
}
constexpr uint32_t row_from(uint64_t pos)
{
return (pos >> 32) & 0xFFFFFFFF;
}
constexpr uint32_t col_from(uint64_t pos)
{
return pos & 0xFFFFFFFF;
}
template <typename T>
class SparseMatrix
{
uint32_t rows_{}, cols_{};
std::vector<std::pair<uint64_t, T>> nzs_;
public:
SparseMatrix(uint32_t r, uint32_t c, uint32_t nzs = 0)
: rows_{r}, cols_{c}
{
nzs_.reserve(nzs);
}
std::istream& read_non_zeroes_from(std::istream& is, uint32_t n)
{
while(n--)
{
uint32_t r, c;
T value;
if ( (is >> r >> c >> value)  &&  value)            
nzs_.emplace_back(pos_from(r, c), value);
}
return is;
}
std::ostream& write_non_zeroes_to(std::ostream& os)
{
os << nzs_.size() << 'n';
for (auto const& i : nzs_)
os << row_from(i.first) << ' ' << col_from(i.first) << ' ' << i.second << 'n';
return os;
}
friend SparseMatrix operator+ (SparseMatrix const& a, SparseMatrix const& b)
{
assert(a.rows_ == b.rows_  &&  a.cols_ == b.cols_);
SparseMatrix sum(a.rows_, a.cols_, std::max(a.nzs_.size(), b.nzs_.size()));
auto i = a.nzs_.cbegin();
auto j = b.nzs_.cbegin();
auto dest = std::back_inserter(sum.nzs_);
while ( i != a.nzs_.cend()  &&  j != b.nzs_.cend() )
{
if ( i->first < j->first )
{
*dest = *i;
++i;
}
else if ( j->first < i->first )
{
*dest = *j;
++j;
}
else
{
auto sum_value = i->second + j->second;
if (sum_value)
*dest = {i->first, sum_value};
++i;
++j;   
}
}
std::copy(i, a.nzs_.cend(), dest);
std::copy(j, b.nzs_.cend(), dest);
return sum;
}
};
int main()
{
uint32_t n, m, N1, N2;
if ( !(std::cin >> n >> m >> N1 >> N2) )
std::cerr << "Error: unable to read initial data.n";
SparseMatrix<int32_t> a(n, m, N1);
if ( !a.read_non_zeroes_from(std::cin, N1) )
std::cerr << "Error: unable to read triplets of the first matrixn";
SparseMatrix<int32_t> b(n, m, N2);
if ( !b.read_non_zeroes_from(std::cin, N2) )
std::cerr << "Error: unable to read triplets of the second matrix.n";
auto c = a + b;
c.write_non_zeroes_to(std::cout);
}

好吧,我只是设法达到了 100 分。基本上,我的错误是我定义operator<的方式,或者在我在这里发布的源代码中(从那时起我重写了几次),事实上我写了一个错误的if陈述,而不是while陈述。这是新的来源:

#include <fstream>
using namespace std;
ifstream cin("matrice_rara.in");
ofstream cout("matrice_rara.out");
struct matrixEntry {
int l, c;
int val;
} matrix1[300001], matrix2[300001], finalMatrix[300001];
bool operator< (const matrixEntry &a, const matrixEntry &b)
{
return a.l < b.l || (a.l == b.l && a.c < b.c);
}
bool operator> (const matrixEntry &a, const matrixEntry &b)
{
return a.l > b.l || (a.l == b.l && a.c > b.c);
}
int matrixSum(int matrix1Size, int matrix2Size)
{
int i = 1, j = 1, k = 0;
while (i <= matrix1Size && j <= matrix2Size)
{
if (matrix1[i] < matrix2[j])
finalMatrix[++k] = matrix1[i++];
else if (matrix1[i] > matrix2[j])
finalMatrix[++k] = matrix2[j++];
else
{
finalMatrix[++k] = matrix1[i++];
finalMatrix[k].val += matrix2[j++].val;
if (finalMatrix[k].val == 0)
k--;
}
}
while (i <= matrix1Size)
finalMatrix[++k] = matrix1[i++];
while (j <= matrix2Size)
finalMatrix[++k] = matrix2[j++];
return k;
}
int main()
{
int unNul1, unNul2, lin, col;
cin >> lin >> col >> unNul1 >> unNul2;
for (int i = 1; i <= unNul1; i++)
cin >> matrix1[i].l >> matrix1[i].c >> matrix1[i].val;
for (int i = 1; i <= unNul2; i++)
cin >> matrix2[i].l >> matrix2[i].c >> matrix2[i].val;
int unNull = matrixSum(unNul1, unNul2);
cout << unNull << "n";
for (int i = 1; i <= unNull; i++)
{
cout << finalMatrix[i].l << " " << finalMatrix[i].c << " " << finalMatrix[i].val << "n";
}
}