Fill the two-dimensional matrix with zeros by inverting groups of cells

There is a problem when I need to fill an array with zeros with the following assumptions:

  • there can be only 0 and 1 in the array
  • we can only change 0 to 1 and 1 to 0
  • when we meet 1 in the array, we must change it to 0 , so that its neighbors also change, for example, for an array like the one below:
 1 0 1 1 1 1 0 1 0 

When we change the element to (1,1), we get an array as follows:

 1 1 1 0 0 0 0 0 0 
  • We cannot change the first line
  • We can only change the elements in the array
  • The end result is the number of times we need to change 1 to 0 to zero the array

1) First example: an array looks like this:

 0 1 0 1 1 1 0 1 0 

the answer is 1.

2) Second example: an array looks like this:

 0 1 0 0 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 0 0 1 0 1 0 1 0 0 

Answer: 10.

There may also be situations where it is impossible to nullify the array, then the answer should be "impossible".

Somehow I can’t get this to work: for the first example, I got the correct answer (1), but for the second example, the program says impossible instead of 10.

Any ideas what is wrong in my code?

 #include <iostream> using namespace std; int main(int argc, char **argv) { int n,m; cin >> n >> m; bool tab[n][m]; for(int i=0; i<n; i++) for(int j=0; j<m; j++) cin >> tab[i][j]; int counter = 0; for(int i=0; i<n-1; i++) { for(int j=0; j<m-1; j++) { if(tab[i][j] == 1 && i > 0 && j > 0) { tab[i-1][j] = !tab[i-1][j]; tab[i+1][j] = !tab[i+1][j]; tab[i][j+1] = !tab[i][j+1]; tab[i][j-1] = !tab[i][j-1]; tab[i][j] = !tab[i][j]; counter ++; } } } bool impossible = 0; for(int i=0; i<n; i++) { for(int j=0; j<m; j++) { if(tab[i][j] == 1) { cout << "impossible\n"; impossible = 1; break; } } if(impossible) break; } if(!impossible) cout << counter << "\n"; return 0; } 
+8
c ++ arrays algorithm
source share
6 answers

I believe that the reason your program came back impossible in the 6x8 matrix is ​​because you walked in the order from left to right / top to bottom, replacing every instance of 1 that you came across with 0 Although this might seem like the right solution, everything what has been done is the scatter of 1s and 0s around the matrix by changing its neighboring values. I think the approach to this problem is to start from the bottom up / right / left and press 1s towards the first line. To some extent, having cornered (capturing) them until they can eliminate them.

Anyway, here is my solution to this problem. I'm not quite sure that this is what you were following, but I think this does the work for the three matrices you presented. The code is not very complicated, and it would be nice to check it with some more difficult problems to make sure that it really works.

 #include <iostream> static unsigned counter = 0; template<std::size_t M, std::size_t N> void print( const bool (&mat) [M][N] ) { for (std::size_t i = 0; i < M; ++i) { for (std::size_t j = 0; j < N; ++j) std::cout<< mat[i][j] << " "; std::cout<<std::endl; } std::cout<<std::endl; } template<std::size_t M, std::size_t N> void flipNeighbours( bool (&mat) [M][N], unsigned i, unsigned j ) { mat[i][j-1] = !(mat[i][j-1]); mat[i][j+1] = !(mat[i][j+1]); mat[i-1][j] = !(mat[i-1][j]); mat[i+1][j] = !(mat[i+1][j]); mat[i][j] = !(mat[i][j]); ++counter; } template<std::size_t M, std::size_t N> bool checkCornersForOnes( const bool (&mat) [M][N] ) { return (mat[0][0] || mat[0][N-1] || mat[M-1][0] || mat[M-1][N-1]); } template<std::size_t M, std::size_t N> bool isBottomTrue( bool (&mat) [M][N], unsigned i, unsigned j ) { return (mat[i+1][j]); } template<std::size_t M, std::size_t N> bool traverse( bool (&mat) [M][N] ) { if (checkCornersForOnes(mat)) { std::cout<< "-Found 1s in the matrix corners." <<std::endl; return false; } for (std::size_t i = M-2; i > 0; --i) for (std::size_t j = N-2; j > 0; --j) if (isBottomTrue(mat,i,j)) flipNeighbours(mat,i,j); std::size_t count_after_traversing = 0; for (std::size_t i = 0; i < M; ++i) for (std::size_t j = 0; j < N; ++j) count_after_traversing += mat[i][j]; if (count_after_traversing > 0) { std::cout<< "-Found <"<<count_after_traversing<< "> 1s in the matrix." <<std::endl; return false; } return true; } #define MATRIX matrix4 int main() { bool matrix1[3][3] = {{1,0,1}, {1,1,1}, {0,1,0}}; bool matrix2[3][3] = {{0,1,0}, {1,1,1}, {0,1,0}}; bool matrix3[5][4] = {{0,1,0,0}, {1,0,1,0}, {1,1,0,1}, {1,1,1,0}, {0,1,1,0}}; bool matrix4[6][8] = {{0,1,0,0,0,0,0,0}, {1,1,1,0,1,0,1,0}, {0,0,1,1,0,1,1,1}, {1,1,0,1,1,1,0,0}, {1,0,1,1,1,0,1,0}, {0,1,0,1,0,1,0,0}}; std::cout<< "-Problem-" <<std::endl; print(MATRIX); if (traverse( MATRIX ) ) { std::cout<< "-Answer-"<<std::endl; print(MATRIX); std::cout<< "Num of flips = "<<counter <<std::endl; } else { std::cout<< "-The Solution is impossible-"<<std::endl; print(MATRIX); } } 

Output for matrix1 :

 -Problem- 1 0 1 1 1 1 0 1 0 -Found 1s in the matrix corners. -The Solution is impossible- 1 0 1 1 1 1 0 1 0 

Output for matrix2 :

 -Problem- 0 1 0 1 1 1 0 1 0 -Answer- 0 0 0 0 0 0 0 0 0 Num of flips = 1 

Output for matrix3 :

 -Problem- 0 1 0 0 1 0 1 0 1 1 0 1 1 1 1 0 0 1 1 0 -Found <6> 1s in the matrix. -The Solution is impossible- 0 1 1 0 1 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 

The output for matrix4 (which relates to your original question):

 -Problem- 0 1 0 0 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 0 0 1 0 1 0 1 0 0 -Answer- 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Num of flips = 10 
+7
source share

Well, here is my slightly different attempt.

Idea

Note. I assume that “We cannot change the first line” means “We cannot change the very top line”.

Some terminology:

  • With bit switching, I mean changing its value from 0 to 1 or from 1 to 0.
  • By flipping a bit, I mean switching the bit and 4 bits around it.

The act of switching bits is commutative. That is, it does not matter in which order we switch it - the final result will always be the same (this is a trivial statement). This means that flipping is also a commutative action, and we can flip the bits in any order we like.

The only way to switch the value at the edge of the matrix is ​​to flip the bit next to it in an uneven number of times. Since we are looking for the lowest flips, we want to flip it a maximum of 1 time. So, in a scenario like the one below, x will need to be flipped exactly once, and y will need to be flipped exactly 0 times.

 . . 1 x 0 y . , 

Two conclusions can be drawn from this:

  • The angle of the matrix can never be switched - if 1 is found on the corner, it is impossible to make the matrix zero with any number of turns. Thus, your first example can be discarded without even flipping one bit.

  • the bit next to the corner should have the same meaning as the bit on the other side. This matrix , which you posted in a comment, can thus also be discarded without dragging one bit (lower right corner).

Two examples of the above conditions:

 0 1 . 0 x . . . . 

It is impossible, since x needs to be flipped exactly once and exactly zero time.

 0 1 . 1 x . . . . 

Perhaps x needs to be flipped exactly once.


Algorithm

Now we can make a recursive argument, and I propose the following:

  • We are given a matrix m by n .
  • Check the conditions of the angle above as indicated above (i.e. corner! = 1, the bit next to the angle should be the same value). If both criteria are violated, return impossible .
  • Scroll to the edge of the matrix. If 1 occurs, flip the nearest bit inside and add 1 to the counter.
  • Now reload from # 1 using the matrix m - 2 by n - 2 (top and bot rows are deleted, left and right columns) if any dimension is> 2, otherwise print the counter and close.

Implementation

Initially, I thought it would turn out beautifully and beautifully, but as they say, it is a little more cumbersome than I initially thought it would be the way we should track a lot of indexes. Ask questions if you are wondering about implementation, but in essence it is a pure translation of the steps described above.

 #include <iostream> #include <vector> using Matrix = std::vector<std::vector<int>>; void flip_bit(Matrix& mat, int i, int j, int& counter) { mat[i][j] = !mat[i][j]; mat[i - 1][j] = !mat[i - 1][j]; mat[i + 1][j] = !mat[i + 1][j]; mat[i][j - 1] = !mat[i][j - 1]; mat[i][j + 1] = !mat[i][j + 1]; ++counter; } int flip(Matrix& mat, int n, int m, int p = 0, int counter = 0) { // I use p for 'padding', ie 0 means the full array, 1 means the outmost edge taken away, 2 the 2 most outmost edges, etc. // max indices of the sub-array int np = n - p - 1; int mp = m - p - 1; // Checking corners if (mat[p][p] || mat[np][p] || mat[p][mp] || mat[np][mp] || // condition #1 (mat[p + 1][p] != mat[p][p + 1]) || (mat[np - 1][p] != mat[np][p + 1]) || // condition #2 (mat[p + 1][mp] != mat[p][mp - 1]) || (mat[np - 1][mp] != mat[np][mp - 1])) return -1; // We walk over all edge values that are *not* corners and // flipping the bit that are *inside* the current bit if it 1 for (int j = p + 1; j < mp; ++j) { if (mat[p][j]) flip_bit(mat, p + 1, j, counter); if (mat[np][j]) flip_bit(mat, np - 1, j, counter); } for (int i = p + 1; i < np; ++i) { if (mat[i][p]) flip_bit(mat, i, p + 1, counter); if (mat[i][mp]) flip_bit(mat, i, mp - 1, counter); } // Finished or flip the next sub-array? if (np == 1 || mp == 1) return counter; else return flip(mat, n, m, p + 1, counter); } int main() { int n, m; std::cin >> n >> m; Matrix mat(n, std::vector<int>(m, 0)); for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { std::cin >> mat[i][j]; } } int counter = flip(mat, n, m); if (counter < 0) std::cout << "impossible" << std::endl; else std::cout << counter << std::endl; } 

Exit

 3 3 1 0 1 1 1 1 0 1 0 

impossible

 3 3 0 1 0 1 1 1 0 1 0 

one

 6 8 0 1 0 0 0 0 0 0 1 1 1 0 1 0 1 0 0 0 1 1 0 1 1 1 1 1 0 1 1 1 0 0 1 0 1 1 1 0 1 0 0 1 0 1 0 1 0 0 

10

 4 6 0 1 0 0 1 0 1 0 1 1 0 1 1 1 1 0 1 1 1 0 

impossible

+4
source share

If the [0] [j] tab is 1, you must switch the [1] [j] tab to clear it. Then you cannot switch line 1 without clearing line 0. Thus, it looks like a reduction step. You repeat the step until one line remains. If this last line is unclear by luck, my intuition is that this is an “impossible” case.

 #include <memory> template <typename Elem> class Arr_2d { public: Arr_2d(unsigned r, unsigned c) : rows_(r), columns_(c), data(new Elem[rows_ * columns_]) { } Elem * operator [] (unsigned row_idx) { return(data.get() + (row_idx * columns_)); } unsigned rows() const { return(rows_); } unsigned columns() const { return(columns_); } private: const unsigned rows_, columns_; std::unique_ptr<Elem []> data; }; inline void toggle_one(bool &b) { b = !b; } void toggle(Arr_2d<bool> &tab, unsigned row, unsigned column) { toggle_one(tab[row][column]); if (column > 0) toggle_one(tab[row][column - 1]); if (row > 0) toggle_one(tab[row - 1][column]); if (column < (tab.columns() - 1)) toggle_one(tab[row][column + 1]); if (row < (tab.rows() - 1)) toggle_one(tab[row + 1][column]); } int solve(Arr_2d<bool> &tab) { int count = 0; unsigned i = 0; for ( ; i < (tab.rows() - 1); ++i) for (unsigned j = 0; j < tab.columns(); ++j) if (tab[i][j]) { toggle(tab, i + 1, j); ++count; } for (unsigned j = 0; j < tab.columns(); ++j) if (tab[i][j]) // Impossible. return(-count); return(count); } unsigned ex1[] = { 0, 1, 0, 1, 1, 1, 0, 1, 0 }; unsigned ex2[] = { 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0 }; Arr_2d<bool> load(unsigned rows, unsigned columns, const unsigned *data) { Arr_2d<bool> res(rows, columns); for (unsigned i = 0; i < rows; ++i) for (unsigned j = 0; j < columns; ++j) res[i][j] = !!*(data++); return(res); } #include <iostream> int main() { { Arr_2d<bool> tab = load(3, 3, ex1); std::cout << solve(tab) << '\n'; } { Arr_2d<bool> tab = load(6, 8, ex2); std::cout << solve(tab) << '\n'; } return(0); } 
+2
source share

The problem is set as follows:

  y yxy If you flip x, then you have to flip all the ys y 

But this is easy if you think of it this way:

  x yyy If you flip x, then you have to flip all the ys y 

This is the same, but now the solution is obvious. You must flip all 1 in line 0, which will flip some bits in lines 1 and 2, then you must flip all 1 in line 1, etc. until you reach the end.

+1
source share

If this is truly a Lights Out game, then there are many resources that detail how to solve the game. It is also likely that this is a duplicate. The game algorithm lights up , as mentioned by other posters.

Let's see if we can solve the first puzzle created, and at least provide a specific description of the algorithm.

The original puzzle seems to be solvable:

 1 0 1 1 1 1 0 1 0 

The trick is that you can clear 1 on the top line by changing the values ​​in the line below them. I will give the coordinates for the row and column using an offset based on 1, which means that the upper left value is (1, 1) and the lower right value is (3, 3).

Change (2, 1), then (2, 3), then (3, 2). I will show the intermediate states of the board with * to change the cell in the next step.

 1 0 1 (2,1) 0 0 1 (2,3) 0 0 0 (3, 2) 0 0 0 * 1 1 ------> 0 0 * ------> 0 1 0 ------> 0 0 0 0 1 0 1 1 0 1 * 1 0 0 0 

This board can be solved, and the number of moves is 3.

The pseudo-algorithm is as follows:

 flipCount = 0 for each row _below_ the top row: for each element in the current row: if the element in the row above is 1, toggle the element in this row: increment flipCount if the board is clear, output flipCount if the board isnt clear, output "Impossible" 

I hope this helps; If necessary, I can clarify, but this is the core of a standard lighting solution. By the way, this is due to the elimination of Gauss; linear algebra appears in some odd situations :)

Finally, in terms of what is wrong with your code, it looks like the following loop:

 for(int i=0; i<n-1; i++) { for(int j=0; j<m-1; j++) { if(tab[i][j] == 1 && i > 0 && j > 0) { tab[i-1][j] = !tab[i-1][j]; tab[i+1][j] = !tab[i+1][j]; tab[i][j+1] = !tab[i][j+1]; tab[i][j-1] = !tab[i][j-1]; tab[i][j] = !tab[i][j]; counter ++; } } } 

There were a few problems for me, but the first assumptions again:

  • I refer to the i-th line and there are n lines
  • j refers to the jth column and there are m columns
  • Now I mean indexes starting with 0 instead of 1

If so, then the following is observed:

  • You can run the for loop i from 1 instead of 0, which means you no longer need to check if i> 0 in the if statement
  • You must leave the value for j> 0 in the if statement; this check means you cannot flip anything in the first column
  • You need to change n-1 in the for i loop, since you need to run it for the last line
  • You need to change m-1 in the for j loop, since you need to run it for the last column (see also point 2).
  • You need to check the cell in the line above the current line, so you need to check the [i-1] [j] == 1 tab
  • Now you need to add border tests for j-1, j + 1 and i + 1 to avoid reading outside the valid ranges of the matrix

Put them together and you have:

 for(int i=1; i<n; i++) { for(int j=0; j<m; j++) { if(tab[i-1][j] == 1) { tab[i-1][j] = !tab[i-1][j]; if (i+1 < n) tab[i+1][j] = !tab[i+1][j]; if (j+1 < m) tab[i][j+1] = !tab[i][j+1]; if (j > 0) tab[i][j-1] = !tab[i][j-1]; tab[i][j] = !tab[i][j]; counter ++; } } } 
+1
source share

A small class that can accept as an input file or check all possible combinations for the first line with only zeros, on 6.5 matrices:

 #include <iostream> #include <fstream> #include <vector> #include <string> #include <cstdlib> #include <ctime> typedef std::vector< std::vector<int> > Matrix; class MatrixCleaner { public: void swapElement(int row, int col) { if (row >= 0 && row < (int)matrix.size() && col >= 0 && col < (int)matrix[row].size()) matrix[row][col] = !matrix[row][col]; } void swapElements(int row, int col) { swapElement(row - 1, col); swapElement(row, col - 1); swapElement(row, col); swapElement(row, col + 1); swapElement(row + 1, col); } void printMatrix() { for (auto &v : matrix) { for (auto &val : v) { std::cout << val << " "; } std::cout << "\n"; } } void loadMatrix(std::string path) { std::ifstream fileStream; fileStream.open(path); matrix.resize(1); bool enconteredNumber = false; bool skipLine = false; bool skipBlock = false; for (char c; fileStream.get(c);) { if (skipLine) { if (c != '*') skipBlock = true; if (c != '\n') continue; else skipLine = false; } if (skipBlock) { if (c == '*') skipBlock = false; continue; } switch (c) { case '0': matrix.back().push_back(0); enconteredNumber = true; break; case '1': matrix.back().push_back(1); enconteredNumber = true; break; case '\n': if (enconteredNumber) { matrix.resize(matrix.size() + 1); enconteredNumber = false; } break; case '#': if(!skipBlock) skipLine = true; break; case '*': skipBlock = true; break; default: break; } } while (matrix.size() > 0 && matrix.back().empty()) matrix.pop_back(); fileStream.close(); } void loadRandomValidMatrix(int seed = -1) { //Default matrix matrix = { { 0,0,0,0,0 }, { 0,0,0,0,0 }, { 0,0,0,0,0 }, { 0,0,0,0,0 }, { 0,0,0,0,0 }, { 0,0,0,0,0 }, }; int setNum = seed; if(seed < 0) if(seed < -1) setNum = std::rand() % -seed; else setNum = std::rand() % 33554432; for (size_t r = 1; r < matrix.size(); r++) for (size_t c = 0; c < matrix[r].size(); c++) { if (setNum & 1) swapElements(r, c); setNum >>= 1; } } bool test() { bool retVal = true; for (int i = 0; i < 33554432; i++) { loadRandomValidMatrix(i); if( (i % 1000000) == 0 ) std::cout << "i= " << i << "\n"; if (clean() < 0) { // std::cout << "x"; std::cout << "\n" << i << "\n"; retVal = false; break; } else { // std::cout << "."; } } return retVal; } int clean() { int numOfSwaps = 0; try { for (size_t r = 1; r < matrix.size(); r++) { for (size_t c = 0; c < matrix[r].size(); c++) { if (matrix.at(r - 1).at(c)) { swapElements(r, c); numOfSwaps++; } } } } catch (...) { return -2; } if (!matrix.empty()) for (auto &val : matrix.back()) { if (val == 1) { numOfSwaps = -1; break; } } return numOfSwaps; } Matrix matrix; }; int main(int argc, char **argv) { std::srand(std::time(NULL)); MatrixCleaner matrixSwaper; if (argc > 1) { matrixSwaper.loadMatrix(argv[argc - 1]); std::cout << "intput:\n"; matrixSwaper.printMatrix(); int numOfSwaps = matrixSwaper.clean(); std::cout << "\noutput:\n"; matrixSwaper.printMatrix(); if (numOfSwaps > 0) std::cout << "\nresult = " << numOfSwaps << " matrix is clean now " << std::endl; else if (numOfSwaps == 0) std::cout << "\nresult = " << numOfSwaps << " nothing to clean " << std::endl; else std::cout << "\nresult = " << numOfSwaps << " matrix cannot be clean " << std::endl; } else { std::cout << "Testing "; if (matrixSwaper.test()) std::cout << " PASS\n"; else std::cout << " FAIL\n"; } std::cin.ignore(); return 0; } 
0
source share

All Articles