【问题标题】:Finding a solution for lights out game using greedy method(backtracking)使用贪心法寻找熄灯游戏的解决方案(回溯)
【发布时间】:2020-04-08 04:16:56
【问题描述】:

我正在尝试使用回溯方法找到熄灯游戏的解决方案。我无法理解此过程的算法。我的方法是枚举从 0 到 2n2 - 1 的所有整数,并将每个整数转换为具有 n*n 位的二进制数。然后,将其分成 n2 个二进制数字(0 表示关灯,1 表示开灯)并分配到一个 n × n 的网格中,例如: 我写了以下代码:-

    void find_solution(int dest[][MAX_SIZE], int size) {

    int y = pow(size,size);
    int remainder;

    for (int x = 0; x<pow(2,y); x++){
        int i = 1;
        int binary_number = 0;
        int n = x;
        while (n!=0) {
            remainder = n%2;
            n/=2;
            binary_number += remainder*i;
            i *= 10;
        }
        int binary_number_digits[size][size];
        for (int k = 0; k<size; k++) {
            for (int l = 0; l<size; l++) {
                binary_number_digits[k][l] = binary_number%10;
                binary_number/=10;
            }
        }
        int count = 0;
        for (int i = 0; i<size; i++) {
            for (int j = 0; j<size; j++) {
                if (binary_number_digits[i][j] == dest[i][j]) {
                    count++;
                }
                if (count <= 4 && count > 0) {
                    if (binary_number_digits[i][j] == 1) {
                        cout << i << j;
                    }
                }
            }
        }
    }
}

我已将十进制数字转换为二进制数字并将其存储在一个数组中并检查它们是否与随机生成的 n*n 网格匹配。如果它是 1,则打印该坐标(x,y)。任何人都可以帮我解决这个算法的问题。谢谢!

【问题讨论】:

  • 这是你说的熄灯游戏吗? en.wikipedia.org/wiki/Lights_Out_(game)
  • 嗨。如果您可以提供一些测试证据,即输入/输出/预期结果/实际结果,这可能会很有用。您可以考虑将代码分成更小的块,以便确定哪个部分不起作用。
  • @Atty 正确。但是,我正在为基本上 2*2、3*3 和 4*4 的网格实现这个游戏。
  • 我不明白你的算法背后的想法。您基本上是在枚举所有可能的董事会状态。这如何帮助您确定必须按什么顺序将灯全部关闭?
  • 你没有解释这个蛮力算法有什么问题(不是贪婪也不是回溯)。它可能很慢,但这个算法应该可以提供答案,除非实现存在问题。

标签: c++ algorithm


【解决方案1】:

需要以下观察(如Wiki 中所列):

  • 在解决方案中,每个灯最多只能按一次。这是因为按奇数次相当于按一次,按偶数次相当于不按。

  • 按下灯的顺序无关紧要。这源于上一点:切换一盏灯会改变它的邻居,但对于邻居的最终结果,只有改变偶数或奇数次才重要。

由此我们可以得出结论,我们可以将解决方案表示为与棋盘大小相同的 0-1 矩阵,其中 1 表示在解决方案中该位置的灯应该被按下。然后,蛮力算法是检查所有 nxn 0-1 矩阵,看看是否有任何一个解决了初始棋盘。

在您的实现中,您执行第一步(生成所有表示按下灯的方式的 nxn 0-1 矩阵)。您错过了检查其中哪些解决了董事会的步骤。

我会使用std::bitset 稍微简化二进制数的处理。

(以下代码对于std::optional也需要C++17。)

#include <bitset>
#include <iostream>
#include <optional>
#include <random>

template <size_t N>
class board_t {
public:
  void print() const {

    for (size_t i = 0; i < data.size(); i++) {
      std::cout << data[i];

      if (i % N == N - 1) {
        std::cout << std::endl;
      }
    }
  }

  void randomize() {
    std::random_device device;
    std::default_random_engine generator{device()};
    std::bernoulli_distribution bernoulli(0.5);

    for (size_t i = 0; i < data.size(); i++) {
      data[i] = bernoulli(generator);
    }
  }

  /**
   * Brute-force all possible ways of pressing the lights.
   */
  std::optional<board_t<N>> solve() const {
    board_t<N> press{};

    do {
      board_t<N> applied{this->apply(press)};

      if (applied.data.none()) {
        return press;
      }

      press.increment();

      /**
       * Aborts when incrementing press overflows back to the initial
       * solution of not pressing any lamp.
       */
    } while (press.data.any());

    /**
     * Return empty std::optional when no solution was found.
     */
    return {};
  }

private:
  /**
   * Indicates which lights are on.
   */
  std::bitset<N * N> data;

  /**
   * Interpret the board as a N*N bit binary number and increment it by one.
   */
  void increment() {
    for (size_t i = 0; i < data.size(); i++) {
      if (data[i]) {
        data[i] = false;
      } else {
        data[i] = true;
        break;
      }
    }
  }

  /**
   * Press each light indicated by press.
   */
  board_t<N> apply(const board_t<N>& press) const {
    board_t<N> copy{*this};

    for (size_t y = 0; y < N; y++) {
      for (size_t x = 0; x < N; x++) {

        size_t offset = x + y * N;

        if (press.data[offset]) {
          copy.data.flip(offset);

          /**
           * Check neighbors.
           */
          if (x > 0) {
            copy.data.flip(offset - 1);
          }

          if (x < N - 1) {
            copy.data.flip(offset + 1);
          }

          if (y > 0) {
            copy.data.flip(offset - N);
          }

          if (y < N - 1) {
            copy.data.flip(offset + N);
          }
        }
      }
    }

    return copy;
  }
};

int main(void) {

  constexpr size_t N = 3;

  board_t<N> board{};

  board.randomize();
  board.print();

  auto solution{board.solve()};

  if (solution) {
    std::cout << "Solution:" << std::endl;
    solution->print();
  } else {
    std::cout << "No solution!" << std::endl;
  }

  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-09
    • 2021-03-30
    • 2016-06-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多