Problem
In this HackerRank problem we are asked to rotate the elements of a matrix in the following way:
Example Input and Output
First and second entry specify the size of the matrix and third entry how many times it has to be rotated. As an example, the following input:
4 4 2
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
would produce the following output:
3 4 8 12
2 11 10 16
1 7 6 15
5 9 13 14
Solution
My idea was to rotate these rings.
#include <vector>
#include <iostream>
#include <algorithm>
using Matrix = std::vector<std::vector<int>>;
void rotate_mat (Matrix &mat, int r){
auto m = mat.size(); // Number of rows
auto n = mat[0].size(); // Number of columns
auto n_rings = std::min(m,n)/2; // Number of rings
for(auto ring_i=0; ring_i<n_rings; ++ring_i){
// The elements of the ring are stored sequentially
// in v_ring so it can be rotated with std::rotate
std::vector<int> v_ring;
// v_ring_ptr points to the original places in the matrix,
// so the rotation of v_ring can be assigned back to the matrix
std::vector<int*> v_ring_ptr;
// Top side of the ring
for(auto j=ring_i; j<=(n-1)-ring_i; ++j) {
v_ring.push_back(mat[ring_i][j]);
v_ring_ptr.push_back(&mat[ring_i][j]);
}
// Right side of the ring
for(auto i=ring_i+1; i<=(m-1)-ring_i; ++i) {
v_ring.push_back(mat[i][(n-1)-ring_i]);
v_ring_ptr.push_back(&mat[i][(n-1)-ring_i]);
}
// Bottom size of the ring
for(auto j=(n-1)-ring_i-1; j>ring_i; --j) {
v_ring.push_back(mat[(m-1)-ring_i][j]);
v_ring_ptr.push_back(&mat[(m-1)-ring_i][j]);
}
// Left size of the ring
for(auto i=(m-1)-ring_i; i>ring_i; --i) {
v_ring.push_back(mat[i][ring_i]);
v_ring_ptr.push_back(&mat[i][ring_i]);
}
std::rotate(v_ring.begin(),v_ring.begin()+r%v_ring.size(),v_ring.end());
// Update the rotated values in the original matrix
for (auto i=0; i<v_ring.size(); ++i){
*v_ring_ptr[i] = v_ring[i];
}
}
};
Matrix read_matrix(int m, int n) {
Matrix mat;
mat.reserve(m);
for(auto i=0; i<m; ++i) {
mat.push_back(std::vector<int>{});
mat[i].reserve(n);
for(auto j=0; j<n; ++j) {
int x; std::cin >> x;
mat[i].push_back(x);
}
}
return mat;
};
void print_matrix(Matrix &mat){
for (auto& i : mat){
for (auto& j : i) {
std::cout << j << " ";
}
std::cout << "\n";
}
};
int main() {
int m,n; std::cin >> m >> n;
int r; std::cin >> r;
auto mat = read_matrix(m,n);
rotate_mat(mat,r);
print_matrix(mat);
return 0;
}
Questions
My main problem is that I would like to avoid having the v_ring
copy and this part:
for (auto i=0; i<v_ring.size(); ++i){
*v_ring_ptr[i] = v_ring[i];
}
Is there a way to store references to the content of the original matrix so the transformation in the constructed rings would automatically be reflected? I tried with std::reference_wrapper
. It almost works with that, I can modify the elements in the ring for example with ++
operator, but I cannot make assigments, nor std::rotate
has any effect on them.
Any ideas? I'm also interested in any other approach that could come to your mind! I thought this way was easy to express in code
v_ring
and another pointing to the valuesv_ring_ptr
. I would like to be able to have a container that doesn't have the values, but the referenes to the values in the matrix so I can just modify them and the changes get reflected in the matrix. \$\endgroup\$