Решения N-Queens C++

Так что мне нужна помощь с классической проблемой N-Queens.

Команда для запуска программы будет следующей: nqueens N k — где N — размер таблицы (N x N), а k — количество решений.

Так, например, если бы я запустил программу, набрав nqueens 4 1, было бы напечатано следующее.

_ Q _ _

_ _ _ Q

Q _ _ _

_ _ Q _

Однако я не могу понять, как обрабатывать более 1 решения? Как определить более одного решения этой проблемы?

То, что у меня есть до сих пор, ниже:

#include <cstdlib>
#include <iomanip>
#include <cmath>
#include <vector>


using namespace std;

class Board
{
private:
    bool** spaces;
    int size;

public:
    Board(int size)
    {
        this->size = size;
        spaces = new bool*[size];
        for (int i = 0; i < size; ++i)
        {
            spaces[i] = new bool[size];
        }
    }

    bool isSafe(int row, int column, vector<int>& state)
    {
       //check row conflict
       //no need to check for column conflicts
       //because the board is beimg filled column
       //by column
       for(int i = 0; i < column; ++i)
       {
          if(state[i] == row)
             return false;
       }

       //check for diagonal conflicts
       int columnDiff = 0;
       int rowDiff = 0;
       for(int i = 0; i < column; ++i)
       {
          columnDiff = abs(column - i);
          rowDiff = abs(row - state[i]);
          if(columnDiff == rowDiff)
             return false;
       }

       return true;

    }

    int getSize()
    {
        return size;
    }

    bool getSpace(int x, int y)
    {
        return spaces[y][x];
    }

    void setSpace(int x, int y, bool value)
    {
        spaces[y][x] = value;
    }

    Board(Board& other)
    {
        this->size = other.getSize();
        spaces = new bool*[size];
        for (int i = 0; i < size; ++i)
        {
            spaces[i] = new bool[size];
            for (int j = 0; j < size; ++j)
            {
                spaces[i][j] = other.getSpace(j, i);
            }
        }
    }

    void backtrack(vector<int>& state, int board_size)
  {
     int row = 0;
     int column = 0;
     bool backtrack = false;

     while(column < board_size)
     {
        while(row < board_size)
        {
           if(backtrack)
           {
              row = state[column] + 1;
              if(row == board_size)
              {
                 column--; //backtrack more
                 backtrack = true;
                 row = 0;
                 break;
              }

              backtrack = false;
           }

           if(isSafe(row, column, state))
           {
              state[column] = row;
              row = 0;
              column++; //advance
              backtrack = false;
              break;
           }

           else
           {
              if(row == (board_size - 1))
              {
                 column--; //backtrack
                 backtrack = true;
                 row = 0;
              }
              else
              {
                 row++;
              }
           }
        }
     }
  }
};

int print_solutions(Board *board, vector<int>& state, int board_size)
{
   for(int i=0; i < board_size; ++i)
   {
      for(int j=0; j < board_size; ++j)
      {
         if(state[i] == j)
            cout << 'Q' << " ";
         else
            cout << '_' << " ";
      }

      cout << endl;
   }
}   

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        cout << "Usage: nqueens [Board Size] [Number of Solutions]" << endl;
    return 1;
    }

    int board_size = atoi(argv[1]);
    //int solution_count = atoi(argv[2]);
    vector<int> state;
    state.resize(board_size);

    Board *my_board = new Board(board_size);
    my_board->backtrack(state, board_size);

    print_solutions(my_board, state, board_size);

    return 0;
}

person hbranum    schedule 16.11.2013    source источник
comment
Это может оказаться полезным: igorsevo.com/   -  person Eutherpy    schedule 17.11.2013
comment
Что ж, argc < 2, вероятно, должно быть argc < 3, поскольку вы ожидаете два параметра, тогда вы, вероятно, захотите каким-то образом использовать параметр количества решений.   -  person Retired Ninja    schedule 17.11.2013


Ответы (4)


В этом решении я сохранил большую часть исходного подхода и кода:

#include <cstdlib>
#include <iomanip>
#include <cmath>
#include <vector>
#include <iostream>

using namespace std;

class Board
{
private:
    bool** spaces;
    int size;

public:
    Board(int size)
    {
        this->size = size;
        spaces = new bool*[size];
        for (int i = 0; i < size; ++i)
        {
            spaces[i] = new bool[size];
        }
    }

    bool isSafe(int row, int column, vector<int>& state)
    {
       //check row conflict
       //no need to check for column conflicts
       //because the board is beimg filled column
       //by column
       for(int i = 0; i < column; ++i)
       {
          if(state[i] == row)
             return false;
       }

       //check for diagonal conflicts
       int columnDiff = 0;
       int rowDiff = 0;
       for(int i = 0; i < column; ++i)
       {
          columnDiff = abs(column - i);
          rowDiff = abs(row - state[i]);
          if(columnDiff == rowDiff)
             return false;
       }

       return true;

    }

    int getSize()
    {
        return size;
    }

    bool getSpace(int x, int y)
    {
        return spaces[y][x];
    }

    void setSpace(int x, int y, bool value)
    {
        spaces[y][x] = value;
    }

    Board(Board& other)
    {
        this->size = other.getSize();
        spaces = new bool*[size];
        for (int i = 0; i < size; ++i)
        {
            spaces[i] = new bool[size];
            for (int j = 0; j < size; ++j)
            {
                spaces[i][j] = other.getSpace(j, i);
            }
        }
    }

    bool backtrack(vector<int>& state, int& column, int board_size)
  {
     int row = 0;
     bool backtrack = column == board_size;

     while(column < board_size || backtrack)
     {
        {
           if(backtrack)
           {
              if (column == 0)
                 return false;
              column--;
              row = state[column] + 1;
              if(row == board_size)
              {
                 backtrack = true;
                 continue;
              }

              backtrack = false;
           }

           if(isSafe(row, column, state))
           {
              state[column] = row;
              row = 0;
              column++; //advance
              backtrack = false;
              continue;
           }

           else
           {
              if(row == (board_size - 1))
              {
                 backtrack = true;
              }
              else
              {
                 row++;
              }
           }
        }
     }
     return true;
  }
};

void print_solutions(Board *board, vector<int>& state, int board_size)
{
   for(int i=0; i < board_size; ++i)
   {
      for(int j=0; j < board_size; ++j)
      {
         if(state[i] == j)
            cout << 'Q' << " ";
         else
            cout << '_' << " ";
      }

      cout << endl;
   }
    cout << endl;
}   

int main(int argc, char* argv[])
{
    if (argc < 3)
    {
        cout << "Usage: nqueens [Board Size] [Number of Solutions]" << endl;
    return 1;
    }

    int board_size = atoi(argv[1]);
    int solution_count = atoi(argv[2]);
    vector<int> state;
    state.resize(board_size);

    Board *my_board = new Board(board_size);
    int column = 0;
    while (solution_count-- > 0 && my_board->backtrack(state, column, board_size))
        print_solutions(my_board, state, board_size);

    return 0;
}
  • исправлено: ошибка компиляции: cout неизвестен -> #include iostream
  • добавлено: дополнительная новая строка в print_solutions для разделения нескольких решений
  • исправлено: print_solutions печатал транспонированную таблицу
  • исправлено: ошибка компиляции: print_solutions не возвращает значение -> void
  • исправлено: argc проверить
  • добавлено: solution_count поддержка путем перемещения column на место вызова
  • исправлено: дублирование кода возврата (column--, row = 0)
  • исправлено: ненужный внутренний цикл (row < board_size)
  • не исправлено: my_board просочился
  • не исправлено: весь класс Board и его экземпляр не нужны
person jmihalicza    schedule 17.11.2013

Вот мое рекурсивное решение грубой силы. Он не оптимален (без возврата), но неплохо работает для шахматных досок размером до 14 x 14. В рекурсивном методе queensSolution первым аргументом является размер шахматной доски. Второй аргумент кодирует фактическое положение ферзей.

Например, вектор, описывающий положение на картинке, будет {1, 3, 0, 2}. Это означает: в первой строке (отсчет начинается с 0, поэтому это первый элемент вектора) ферзь стоит на позиции 1 (второй квадрат слева), во второй строке (второй элемент вектора) стоит ферзь на позиции 3 (последний квадрат ряда) и т.д.

введите здесь описание изображения

Третий аргумент содержит вектор, который будет содержать все позиции решения (закодированные как вектор, как описано выше). Справочный метод intersect проверяет, есть ли коллизия между новым ферзем, который будет помещен на позицию {queens.size(), x}. Столкновения происходят, когда новый ферзь находится в том же «столбце» (положение x), что и любой из существующих ферзей, или если расстояния между позициями x и y нового ферзя с любым из существующих ферзей равны (положение по диагонали). Нам не нужно проверять, будет ли новый ферзь помещен в строку (y), где уже размещен какой-либо другой ферзь, потому что каждый раз, когда мы добавляем элемент в вектор queens, мы помещаем его в новую строку.

#include<vector>
using namespace std;

bool intersect(const vector<int> &queens, int x) {
    int y = queens.size();
    for (int i = 0; i < y; i++) {
        if ((abs(queens[i] - x) == 0) || (abs(queens[i] - x) == y - i))
            return true;
    }
    return false;
}

void queensSolution(const int dim, vector<int> queens, vector<vector<int>> &res) {
    if (queens.size() >= dim) {
        res.push_back(queens);
        queens.clear();
        return;
    }
    for (int i = 0; i < dim; i++) {
        if (!intersect(queens, i)) {
            queens.push_back(i);
            queensSolution(dim, queens, res);
            queens.pop_back();
        }
    }
}

Например, чтобы найти все решения для шахматной доски 4 x 4, выполните следующие действия:

int main(){
  int dim = 4;
  vector<int>queens;
  vector<vector<int>> result;
  queensSolution(dim, queens, result);
}

После возврата queensSolution векторный результат содержит два вектора: {1, 3, 0, 2} и {2, 0, 3, 1}.

person Andrushenko Alexander    schedule 02.10.2018

Вы можете использовать рекурсивный подход для его решения. Я написал об этом статью: Руководство по рекурсии: N -ферзей в C. Чтобы получить все решения, просто запустите рекурсию без остановки для первого найденного решения.

Здесь также доступно эвристическое решение: головоломка с восьмеркой ферзя.

person Igor Ševo    schedule 17.11.2013

Проверьте этот суть. Это простая рекурсивная функция, которая возвращает все решения. Он работает, помещая каждый раз ферзя в следующий ряд. Метод is_safeпроверяет, безопасно ли размещать ферзя в столбце столбца в следующей строке. Решение представляет собой вектор, где индекс i — это строка, а значение по этому индексу — это столбец. Каждый раз, когда ферзь размещается успешно, вектор увеличивается на один элемент и добавляется к набору возможных решений, которые возвращаются обратно в стек рекурсивных вызовов.

person Imaxd    schedule 02.03.2014