通过翻转单元格组

时间:2016-11-12 12:08:21

标签: c++ arrays algorithm

我需要用零填充数组,但有以下假设:

    数组中的
  • 只能是01
  • 我们只能将0更改为1,将1更改为0
  • 当我们在数组中遇到1时,我们必须将其更改为0,以便其邻居也会更改,例如,对于如下所示的数组:
1 0 1
1 1 1
0 1 0

当我们改变(1,1)处的元素时,我们就得到了这样的数组:

1 1 1
0 0 0
0 0 0
  • 我们无法更改第一行
  • 我们只能更改数组中的元素
  • 最终结果是我们必须将1更改为0以将数组清零的次数

1)第一个例子,数组如下所示:

0 1 0
1 1 1
0 1 0

答案是1。

2)第二个例子,数组如下所示:

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.

也可能存在无法将阵列归零的情况,那么答案应该是“不可能的”#34;。

不知怎的,我无法正常工作:对于第一个例子,我得到了正确答案(1)但是对于第二个例子,程序说impossible而不是10。

我的代码中有什么错误?

#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;
}

6 个答案:

答案 0 :(得分:5)

我相信你的程序在6x8矩阵中返回不可能的原因是因为你一直在以从左到右/从上到下的方式遍历,用0替换你遇到的每个1的实例虽然这似乎是正确的解决方案,但它所做的只是通过修改它的相邻值来分散矩阵周围的1和0。我认为解决这个问题的方法是从下到上/从右到左开始并将1s推向第一行。在某种程度上转弯(捕捉)它们直到它们被消除。

无论如何,这是我解决这个问题的方法。我不完全确定这是否是您要追求的,但我认为它可以完成您提供的三个矩阵的工作。代码不是很复杂,用一些更难的问题来测试代码是否真的有用。

#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);
    }
}

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 

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

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 

输出 matrix4 (解决原始问题):

-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

答案 1 :(得分:4)

好的,这是我的尝试有点不同。

<强>观

注意:我在这里假设&#34;我们无法改变第一行&#34;意味着&#34;我们无法更改 outmost 行&#34;。

一些术语:

  • 使用切换,我的意思是将其值从0更改为1或1更改为0
  • 翻转,我的意思是切换所述位及其周围的4位

切换一下的行为是可交换的。也就是说,我们切换它的顺序无关紧要 - 最终结果总是相同的(这是一个简单的陈述)。这意味着翻转也是一种可交换的动作,我们可以按任意顺序翻转位。

在矩阵边缘切换值的唯一方法是将其旁边的位翻转不均匀的次数。当我们正在寻找尽可能低的翻转时,我们希望最多翻转一次。因此,在如下所示的场景中,x将需要恰好翻转一次,而y将需要恰好翻转0次。

. .
1 x
0 y
. ,

由此我们可以得出两个结论:

  1. 矩阵的一个角永远不会被切换 - 如果找到角上的1,则无法使用任意数量的翻转使矩阵为零。因此,您的第一个示例可以被丢弃,甚至不会翻转一个位。

  2. 角落旁边的位必须与另一侧的位具有相同的值。因此,您在评论中发布的This matrix也可以在不翻转一位(右下角)的情况下被丢弃。

  3. 上述两个条件的例子:

    0 1 .
    0 x .
    . . .
    

    不可能,因为x需要完全翻转正好为零次。

    0 1 .
    1 x .
    . . . 
    

    可能,x需要恰好翻转一次。

    <强>算法

    我们现在可以做一个递归参数,我建议如下:

    1. 我们获得m n矩阵。
    2. 如上所述检查上面的拐角条件(即拐角!= 1,拐角旁边的位必须是相同的值)。如果违反任一条件,请返回impossible
    3. 绕过矩阵的边缘。如果遇到1,则翻转最近的位,并将1加到计数器上。
    4. 如果其中任一维度为&gt;,则从#1开始,使用m - 2n - 2矩阵(删除顶部和机器人行,左右列)重新启动#1 2,否则打印计数器并退出。
    5. <强>实施

      最初我以为这会变得漂亮而漂亮,但事实上它被告知它比我原先认为的那样麻烦,因为我们必须跟踪很多指数。如果您对实施有疑问,请提出问题,但它实质上是对上述步骤的纯粹翻译。

      #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;
      }
      

      <强>输出

      3 3
      1 0 1
      1 1 1
      0 1 0
      
        

      不可能

      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
      
        

      不可能

答案 2 :(得分:2)

如果tab [0] [j]为1,则必须切换制表符[1] [j]才能将其清除。然后你不能在没有清除第0行的情况下切换第1行。所以它似乎是一个减少步骤。重复该步骤,直到剩下一行。如果运气不清楚最后一行,我的直觉是它是不可能的&#34;情况下。

class d():
    def __init__(self):
        self._values = []

    def __pos__(self):
        return self._values[0]

    def append(self, value): # create your won function
        self._values.append(value)

答案 3 :(得分:1)

问题是这样说的:

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

但如果您这样想的话,这很容易:

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

这是同样的事情,但现在解决方案很明显 - 你必须翻转第0行中的所有1,这将翻转第1行和第2行中的一些位,然后你必须翻转第1行中的所有1,等等,直到你走到尽头。

答案 4 :(得分:1)

如果这确实是Lights Out游戏,那么有很多资源可以详细说明如何解决游戏问题。正如其他海报已经提到的那样,这很可能是Lights out game algorithm的重复。

让我们看看我们是否能解决所提供的第一个样本谜题,但至少要提出一个算法的具体描述。

最初的谜题似乎是可以解决的:

1 0 1
1 1 1
0 1 0

诀窍在于,您可以通过更改其下方行中的值来清除顶行中的1。我将使用基于1的偏移量按行和列提供坐标,这意味着左上角值为(1,1),右下角值为(3,3)。

改变(2,1),然后改变(2,3),然后改变(3,2)。我将在下一步中显示电路板的中间状态,其中*将更改电池。

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

此板可以解决,移动次数似乎为3。

伪算法如下:

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"

我希望这会有所帮助;如果需要,我可以进一步详细说明,但这是标准熄灯解决方案的核心。 BTW,它与高斯消除有关;线性代数在某些奇怪的情况下出现:)

最后,就您的代码有什么问题而言,它似乎是以下循环:

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 ++;
        }
    }
}

我遇到了几个问题,但又是第一个假设:

  • 我指的是第i行,有n行
  • j指的是第j列,有m列
  • 我现在指的是从0开始而不是1
  • 的索引

如果是这种情况,则会发现以下情况:

  1. 您可以从1而不是0运行for i循环,这意味着您不再需要检查i&gt; if语句中的0
  2. 你应该删除for j&gt; if语句中为0;该检查意味着您无法翻转第一列中的任何内容
  3. 你需要更改for i循环中的n-1,因为你需要为最后一行运行它
  4. 你需要更改for j循环中的m-1,因为你需要为最后一列运行它(见第2点)
  5. 您需要检查当前行上方行中的单元格,因此您应该检查选项卡[i-1] [j] == 1
  6. 现在你需要为j-1,j + 1和i + 1添加边界测试,以避免读取矩阵的有效范围
  7. 把这些放在一起,你有:

    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 ++;
            }
        }
    }
    

答案 5 :(得分:0)

一个小类,可以作为输入文件或测试所有可能的组合,第一行只有零,在6,5矩阵上:

#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;
}