9

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

  • in the array there can only be 0 and 1
  • we can only change 0 to 1 and 1 to 0
  • when we meet 1 in array, we have to change it to 0, such that its neighbours are also changed, for instance, for the array like the one below:
1 0 1
1 1 1
0 1 0

When we change element at (1,1), we then got the array like this:

1 1 1
0 0 0
0 0 0
  • We can't change the first row
  • We can only change the elements that are in the array
  • The final result is the number of times we have to change 1 to 0 to zero out the array

1) First example, array is like this one below:

0 1 0
1 1 1
0 1 0

the answer is 1.

2) Second example, array is like this one below:

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

The answer is 10.

There also can be situations that its impossible to zero out the array, then the answer should be "impossible".

Somehow I can't get this working: for the first example, I got the right answer (1) but for the second example, program says impossible instead of 10.

Any ideas what's 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;
}
ivan_pozdeev
  • 33,874
  • 19
  • 107
  • 152
Brian Brown
  • 3,873
  • 16
  • 48
  • 79
  • 5
    At first glance, this seems to me to be an order of magnitude more complicated and require more work than the few lines you have already. This could be considered to be an optimisation problem, where you have to try to find the best solution by trying different paths that lead to the desired result. There are certainly some theories about permutations that can be used here, but I'm no mathematician. What you have now, just starts at (1,1) and flips it to 0 if it's 1. So in the first example, you get lucky, but no other is going to work this way. – Karsten Koop Nov 12 '16 at 12:17
  • Do you have to return the shortest solution or just the length of the solution you found? – Karsten Koop Nov 12 '16 at 12:18
  • @KarstenKoop: I need the shortest solution. What do you suggest? – Brian Brown Nov 12 '16 at 12:18
  • I'm not sure, maybe building a tree with possible paths leading to a solution, and checking for loops. If all paths end in loops, there is no solution. But the tree would grow quite fast. – Karsten Koop Nov 12 '16 at 12:25
  • @BrianBrown The right tool to solve such problems is your debugger. You should step through your code line-by-line *before* asking on Stack Overflow. For more help, please read [How to debug small programs (by Eric Lippert)](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/). At a minimum, you should \[edit] your question to include a [Minimal, Complete, and Verifiable](http://stackoverflow.com/help/mcve) example that reproduces your problem, along with the observations you made in the debugger. – πάντα ῥεῖ Nov 12 '16 at 12:25
  • 4
    I think you need to understand the algorithm before you can debug the code, and I'm not sure that threshold has been reached ... – Useless Nov 14 '16 at 15:31
  • Does it have to be a C Array? Is a C++ std::array or std::vector ok too? I think I am able to solve this. Also, what do you mean with "the shortest" solution? Least lines of code, fastest execution time or most understandable code? – Jan Hohenheim Nov 14 '16 at 15:44
  • It looks like there is a misunderstanding of the problem statement somewhere. *when we meet 1 in array, we have to change it to 0* probably does not mean *we must go over the array left to right, top to bottom, and unconditionally change every 1 we encounter to 0*. – n. m. could be an AI Nov 14 '16 at 15:45
  • What does "We can't change the first row" mean exactly? That we cannot flip the bit directly and have to flip his lower neighbor? Can I freely flip a 0 to 1 and back to 0 to flip its neighbors? – Jan Hohenheim Nov 14 '16 at 15:49
  • @JanNilsFerner: it needs to be a C array, like in the example code, STL is not allowed. By the shortest I mean the smallest number of steps to solve this. We can't flip bits in first row "directly", but if we are in the second row, and need to flip the first, then, and only then its allowed. – Brian Brown Nov 14 '16 at 16:04
  • @Useless: I totally agree with you. But somehow I have some problems with coming up with the right solution. Any hint? – Brian Brown Nov 14 '16 at 16:13
  • Well, the problem statement contradicts itself, which doesn't help. You say you can't change the first row, and then do it anyway. Presumably you _can_ change it, but only as a side-effect of changing the second row? You say you want the shortest sequence of changes, but also suggest you _have_ to change every 1 you come across. Can you choose which changes to make or not? – Useless Nov 14 '16 at 16:16
  • Basically, the goal is to fill the array with zeros. I do not need to take every cell, this was only my approach - as it can be seen, not so successfull. By the shortest I meant the smallest number of steps to solve this (to fill the array with zeros). I can change the first row, but as you said, *only as a side-effect of changing the second row*. I was thinking about maybe the backtracking algorithm, but dont know if its the right direction here. – Brian Brown Nov 14 '16 at 16:21
  • The requirement to not change the first row directly is the key to the solution proof (it makes the proof quite simple). However, you said just "the first row", but in your code you don't change the first column directly either, and you test for `i < n - 1`, so you don't change the other ends either. I have a sneaking suspicion that you actually meant you can't change the elements on the borders of the array (all four borders); is that correct? If so, please amend the question to make it clear. – bogdan Nov 14 '16 at 17:51
  • 2
    http://stackoverflow.com/questions/19795973/lights-out-game-algorithm ? – גלעד ברקן Nov 15 '16 at 00:49
  • 2
    ` bool tab[n][m];` is not allowed in Standard C++. Array dimensions must be known at compiletime. – M.M Nov 15 '16 at 01:20
  • result of the program in second case is correct for your specification. 1 at [1,4] coords creates 1 at coords [0,4], which your algorithm will not zero out anymore, because it is the only time this coordinate is visited – Jan Hruby Nov 21 '16 at 13:45
  • basically you can make a stop condition in case that you change element [i-1,j] to 1 – Jan Hruby Nov 21 '16 at 13:59

6 Answers6

5

I believe that the reason your program was returning impossible in the 6x8 matrix is because you have been traversing in a left to right / top to bottom fashion, replacing every instance of 1 you encountered with 0. Although this might have seemed as the right solution, all it did was scatter the 1s and 0s around the matrix by modifying it's neighboring values. I think that the way to approach this problem is to start from bottom to top/ right to left and push the 1s towards the first row. In a way cornering (trapping) them until they can get eliminated.

Anyway, here's my solution to this problem. I'm not entirely sure if this is what you were going after, but I think it does the job for the three matrices you provided. The code is not very sophisticated and it would be nice to test it with some harder problems to see if it truly 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 

Output for matrix4 (which addresses 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
Constantinos Glynos
  • 2,952
  • 2
  • 14
  • 32
  • Solution for the first matrix is wrong. The matrix is not filled with zeros at the end (it has 1s in first row). I have yet another example: https://s12.postimg.org/4fy6fl6gd/shoot1.png where the answer should be `impossible`. – Brian Brown Nov 15 '16 at 08:52
  • @Brian Brown, in the first example, your result has 1s on the top because you are not able to change them in the first row. You didn't specify that it was a wrong answer. Can you please clear this up: "when we meet 1 in array, we have to change it to 0, such that its neighbours are also changed". Are you allowed to change a corner value in the matrix by only flipping 2 neighbours, or does the rule say that you should always flip 4 neighbours? Is the answer to the first example, impossible? – Constantinos Glynos Nov 15 '16 at 09:13
  • We can't flip bits in first row "directly", but if we are in the second row, and need to flip the first, then, and only then its allowed. I can change the first row, but only as a side-effect of changing the second row. – Brian Brown Nov 15 '16 at 09:21
  • @BrianBrown, I understand that, but my question was whether or not you are able to handle the corner or sided values (1s) by fliping only 2 or 3 neighbours, or is there a rule that says you must flip all 4 neighbours. For example, imagine that in the bottom left of a 3x3 matrix you had, M[2][0] = 1, M[1][0] = 0 and M[2][1] = 0, are you liable of flipping M[2][0], so that you get M[2][0] = 0, M[1][0] = 1 and M[2][1] = 1 ? Also, what is the correct answer for the first example? Is it impossible? – Constantinos Glynos Nov 15 '16 at 09:27
  • Amended the code using your method for impossible. Again, what is the answer to the first example? If it is impossible, then this method should give you your expected results. – Constantinos Glynos Nov 15 '16 at 10:18
4

Ok, here comes my somewhat different attempt.

Idea

Note: I assume here that "We can't change the first row" means "We can't change the outmost row".

Some terminology:

  • With toggling a bit I mean changing it's value from 0 to 1 or 1 to 0.
  • With flipping a bit I mean toggling said bit and the 4 bits around it.

The act of toggling a bit is commutative. That is, it does not matter in what order we toggle it—the end result will always be the same (this is a trivial statement). This means that flipping is also a commutative action, and we are free to flip bits in any order we like.

The only way to toggle a value on the edge of the matrix is by flipping the bit right next to it an uneven amount of times. As we're looking for the lowest possible flips, we want to flip it a maximum of 1 time. So, in a scenario like the on below, x will need to be flipped exactly once, and y will need to be flipped exactly 0 times.

. .
1 x
0 y
. ,

From this we can draw two conclusions:

  1. A corner of the matrix can never be toggled—if a 1 on the corner is found it is not possible with any number of flips to make the matrix zero. Your first example can thus be discarded without even flipping a single bit.

  2. A bit next to a corner must have the same same value as the bit on the other side. This matrix that you posted in a comment can thus as well be discarded without flipping a single bit (bottom right corner).

Two examples of the conditions above:

0 1 .
0 x .
. . .

Not possible, as x needs to be flipped exactly once and exactly zero times.

0 1 .
1 x .
. . . 

Possible, x needs to be flipped exactly once.


Algorithm

We can now make an recursive argument, and I propose the following:

  1. We are given an m by n matrix.
  2. Check the corner conditions above as stated above (i.e. corner != 1, bits next to corner has to be the same value). If either criteria are violated, return impossible.
  3. Go around the edge of the matrix. If a 1 is encountered, flip the closest bit inside, and add 1 to the counter.
  4. Restart now from #1 with a m - 2 by n - 2 matrix (top and bot row removed, left and right column) if either dimension is > 2, otherwise print the counter and quit.

Implementation

Initially I had thought this would turn out nice and pretty, but truth be told it is a little more cumbersome than I originally thought it would be as we have to keep track of a lot of indices. Please ask questions if you're wondering about the implementation, but it is in essence a pure translation of the steps 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', i.e. 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's 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;
}

Output

3 3
1 0 1
1 1 1
0 1 0

impossible

3 3
0 1 0
1 1 1
0 1 0

1

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

pingul
  • 3,351
  • 3
  • 25
  • 43
2

If tab[0][j] is 1, you have to toggle tab[1][j] to clear it. You then cannot toggle row 1 without unclearing row 0. So it seems like a reduction step. You repeat the step until there is one row left. If that last row is not clear by luck, my intuition is that it's the "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);
  }
WaltK
  • 724
  • 4
  • 13
1

The problem is stated like this:

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

But it's easy if you think about it like this:

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

It's the same thing, but now the solution is obvious -- You must flip all the 1s in row 0, which will flip some bits in rows 1 and 2, then you must flip all the 1s in row 1, etc, until you get to the end.

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
1

If this is indeed the Lights Out game, then there are plenty of resources that detail how to solve the game. It is also quite likely that this is a duplicate of Lights out game algorithm, as has already been mentioned by other posters.

Let's see if we can't solve the first sample puzzle provided, however, and at least present a concrete description of an algorithm.

The initial puzzle appears to be solvable:

1 0 1
1 1 1
0 1 0

The trick is that you can clear 1's in the top row by changing the values in the row underneath them. I'll provide coordinates by row and column, using a 1-based offset, meaning that the top left value is (1, 1) and the bottom right value is (3, 3).

Change (2, 1), then (2, 3), then (3, 2). I'll show the intermediate states of the board with the * for the cell being changed 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 appears to be 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; I can elaborate further if required but this is the core of the standard lights out solution. BTW, it is related to Gaussian Elimination; linear algebra crops up in some odd situations :)

Finally, in terms of what is wrong with your code, it appears to be 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 ++;
        }
    }
}

Several issues occur to me, but first assumptions again:

  • i refers to the ith row and there are n rows
  • j refers to the jth column and there are m columns
  • I'm now referring to indices that start from 0 instead of 1

If this is the case, then the following is observed:

  1. You could run your for i loop from 1 instead of 0, which means you no longer have to check whether i > 0 in the if statement
  2. You should drop the for j > 0 in the if statement; that check means that you can't flip anything in the first column
  3. You need to change the n-1 in the for i loop as you need to run this for the final row
  4. You need to change the m-1 in the for j loop as you need to run this for the final column (see point 2 also)
  5. You need to check the cell in the row above the current row, so you you should be checking tab[i-1][j] == 1
  6. Now you need to add bounds tests for j-1, j+1 and i+1 to avoid reading outside valid ranges of the matrix

Put these 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 ++;
        }
    }
}
Community
  • 1
  • 1
BMurray
  • 341
  • 2
  • 5
0

A little class that can take as a input file or test all possible combination for first row with only zeros, on 6,5 matrix:

#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;
}
Logman
  • 4,031
  • 1
  • 23
  • 35