📜  使用分支和约束的N Queen问题

📅  最后修改于: 2021-04-24 15:11:24             🧑  作者: Mango

N个皇后难题是将N个国际象棋皇后放置在N×N棋盘上的问题,这样就不会有两个皇后相互威胁。因此,解决方案要求没有两个皇后共享相同的行,列或对角线。

N-Queen的回溯算法已在此处讨论。在回溯解决方案中,当我们遇到死胡同时,我们会回溯。在Branch and Bound解决方案中,在建立了部分解决方案之后,我们发现,要达到死胡同,没有任何更深的意义

让我们从描述回溯解决方案开始。 “我们的想法是将皇后区从最左边的列开始一个一列地放置在不同的列中。将皇后放置在列中时,我们检查是否与已放置的皇后发生冲突。在当前列中,如果找到没有冲突的行,则将该行和列标记为解决方案的一部分。如果由于冲突而找不到这样的行,那么我们将回溯并返回false。”

女王
女王

  1. 对于第一名皇后,共有8种可能性,因为我们可以将第一名皇后放在第一列的任何行中。让我们将女王1放在第3行。
  2. 放置第一位女王后,第二位女王还有7种可能性。但是,等等,我们实际上没有7种可能性。我们不能将皇后2放置在第2、3或4行,因为这些单元格受到皇后1的攻击。因此,皇后2仅剩8 – 3 = 5个有效位置。
  3. 在为Queen 2选好位置后,Queen 3的选择就更少了,因为其列中的大多数单元格都受到前2个Queens的攻击。

我们需要找出一种有效的方法来跟踪受到攻击的单元。在先前的解决方案中,我们保留一个8×8布尔矩阵,并在每次放置一个女王后对其进行更新,但这需要线性时间进行更新,因为我们需要检查安全单元。
基本上,我们必须确保以下四点:
1.没有两个皇后共享一列。
2.没有两个皇后共享一排。
3.没有两个女王共享从右上到左下的对角线。
4.没有两个皇后共享一个从左上到右下的对角线。

由于我们存储解决方案的方式,编号1是自动的。对于数字2、3和4,我们可以在O(1)时间内执行更新。这个想法是保留三个布尔数组,它们告诉我们哪些行和哪些对角线被占用

让我们先做一些预处理。让我们创建两个N x N矩阵,一个用于/对角线,另一个用于\对角线。我们分别称它们为slashCode和backslashCode。诀窍是用这样的方式填充它们:共享相同/对角线的两个皇后在slashCode矩阵中将具有相同的值,如果它们共享相同的\ diagonal,则它们在backslashCode矩阵中将具有相同的值。
对于N x N矩阵,请使用以下公式填充slashCode和backslashCode矩阵–
slashCode [row] [col] =行+ col
backslashCode [row] [col] =行– col +(N-1)

使用上面的公式将得出下面的矩阵

NQueen2

NQueen2

反斜杠代码中的’N – 1’可以确保代码永远不会为负,因为我们将使用这些代码作为数组中的索引。
现在在将女王i放在第j行之前,我们首先检查是否使用了j行(使用数组存储行信息)。然后,我们检查是否使用了斜杠代码(j + i)或反斜杠代码(j – i + 7)(保留两个数组,这些数组将告诉我们哪些对角线被占用)。如果是,那么我们必须为皇后i尝试其他位置。如果不是,则将行和两个对角线标记为已使用,然后在第i + 1位上递归。在递归调用返回之后,在尝试为第i个位置定位另一个位置之前,我们需要将行,斜杠代码和反斜杠代码重置为再次使用,就像前面注释中的代码一样。

以下是上述想法的实现–

C++
/* C++ program to solve N Queen Problem using Branch
   and Bound */
#include
#include
#define N 8
 
/* A utility function to print solution */
void printSolution(int board[N][N])
{
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
            printf("%2d ", board[i][j]);
        printf("\n");
    }
}
 
/* A Optimized function to check if a queen can
be placed on board[row][col] */
bool isSafe(int row, int col, int slashCode[N][N],
            int backslashCode[N][N], bool rowLookup[],
      bool slashCodeLookup[], bool backslashCodeLookup[] )
{
    if (slashCodeLookup[slashCode[row][col]] ||
        backslashCodeLookup[backslashCode[row][col]] ||
        rowLookup[row])
    return false;
 
    return true;
}
 
/* A recursive utility function
to solve N Queen problem */
bool solveNQueensUtil(int board[N][N], int col,
    int slashCode[N][N], int backslashCode[N][N],
                                  bool rowLookup[N],
                            bool slashCodeLookup[],
                           bool backslashCodeLookup[] )
{
    /* base case: If all queens are placed
    then return true */
    if (col >= N)
        return true;
 
    /* Consider this column and try placing
       this queen in all rows one by one */
    for (int i = 0; i < N; i++)
    {
        /* Check if queen can be placed on
           board[i][col] */
        if ( isSafe(i, col, slashCode,
                    backslashCode, rowLookup,
          slashCodeLookup, backslashCodeLookup) )
        {
            /* Place this queen in board[i][col] */
            board[i][col] = 1;
            rowLookup[i] = true;
            slashCodeLookup[slashCode[i][col]] = true;
            backslashCodeLookup[backslashCode[i][col]] = true;
 
            /* recur to place rest of the queens */
            if ( solveNQueensUtil(board, col + 1,
                                  slashCode, backslashCode,
             rowLookup, slashCodeLookup, backslashCodeLookup) )
                return true;
 
            /* If placing queen in board[i][col]
            doesn't lead to a solution, then backtrack */
 
            /* Remove queen from board[i][col] */
            board[i][col] = 0;
            rowLookup[i] = false;
            slashCodeLookup[slashCode[i][col]] = false;
            backslashCodeLookup[backslashCode[i][col]] = false;
        }
    }
 
    /* If queen can not be place in any row in
        this colum col then return false */
    return false;
}
 
/* This function solves the N Queen problem using
Branch and Bound. It mainly uses solveNQueensUtil() to
solve the problem. It returns false if queens
cannot be placed, otherwise return true and
prints placement of queens in the form of 1s.
Please note that there may be more than one
solutions, this function prints one of the
feasible solutions.*/
bool solveNQueens()
{
    int board[N][N];
    memset(board, 0, sizeof board);
 
    // helper matrices
    int slashCode[N][N];
    int backslashCode[N][N];
 
    // arrays to tell us which rows are occupied
    bool rowLookup[N] = {false};
 
    //keep two arrays to tell us
    // which diagonals are occupied
    bool slashCodeLookup[2*N - 1] = {false};
    bool backslashCodeLookup[2*N - 1] = {false};
 
    // initialize helper matrices
    for (int r = 0; r < N; r++)
        for (int c = 0; c < N; c++) {
          slashCode[r] = r + c,
            backslashCode[r] = r - c + 7;
        }
 
    if (solveNQueensUtil(board, 0,
                          slashCode, backslashCode,
      rowLookup, slashCodeLookup, backslashCodeLookup) ==
                                                 false )
    {
        printf("Solution does not exist");
        return false;
    }
 
    // solution found
    printSolution(board);
    return true;
}
 
// Driver program to test above function
int main()
{
    solveNQueens();
 
    return 0;
}


Java
// Java program to solve N Queen Problem
// using Branch and Bound
import java.io.*;
import java.util.Arrays;
 
class GFG{
 
static int N = 8;
 
// A utility function to print solution
static void printSolution(int board[][])
{
    int N = board.length;
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < N; j++)
            System.out.printf("%2d ", board[i][j]);
             
        System.out.printf("\n");
    }
}
 
// A Optimized function to check if a queen
// can be placed on board[row][col]
static boolean isSafe(int row, int col,
                      int slashCode[][],
                      int backslashCode[][],
                      boolean rowLookup[],
                      boolean slashCodeLookup[],
                      boolean backslashCodeLookup[])
{
    if (slashCodeLookup[slashCode[row][col]] ||
        backslashCodeLookup[backslashCode[row][col]] ||
        rowLookup[row])
        return false;
 
    return true;
}
 
// A recursive utility function to
// solve N Queen problem
static boolean solveNQueensUtil(
    int board[][], int col, int slashCode[][],
    int backslashCode[][], boolean rowLookup[],
    boolean slashCodeLookup[],
    boolean backslashCodeLookup[])
{
     
    // Base case: If all queens are placed
    // then return true
    int N = board.length;
     
    if (col >= N)
        return true;
 
    // Consider this column and try placing
    // this queen in all rows one by one
    for(int i = 0; i < N; i++)
    {
        // Check if queen can be placed on board[i][col]
        if (isSafe(i, col, slashCode, backslashCode,
                   rowLookup, slashCodeLookup,
                   backslashCodeLookup))
        {
             
            // Place this queen in board[i][col]
            board[i][col] = 1;
            rowLookup[i] = true;
            slashCodeLookup[slashCode[i][col]] = true;
            backslashCodeLookup[backslashCode[i][col]] = true;
 
            // recur to place rest of the queens
            if (solveNQueensUtil(
                    board, col + 1, slashCode,
                    backslashCode, rowLookup,
                    slashCodeLookup,
                    backslashCodeLookup))
                return true;
 
            // If placing queen in board[i][col] doesn't
            // lead to a solution, then backtrack
 
            // Remove queen from board[i][col]
            board[i][col] = 0;
            rowLookup[i] = false;
            slashCodeLookup[slashCode[i][col]] = false;
            backslashCodeLookup[backslashCode[i][col]] = false;
        }
    }
 
    // If queen can not be place in any row
    // in this colum col then return false
    return false;
}
 
/*
 * This function solves the N Queen problem using Branch
 * and Bound. It mainly uses solveNQueensUtil() to solve
 * the problem. It returns false if queens cannot be
 * placed, otherwise return true and prints placement of
 * queens in the form of 1s. Please note that there may
 * be more than one solutions, this function prints one
 * of the feasible solutions.
 */
static boolean solveNQueens()
{
    int board[][] = new int[N][N];
     
    // Helper matrices
    int slashCode[][] = new int[N][N];
    int backslashCode[][] = new int[N][N];
 
    // Arrays to tell us which rows are occupied
    boolean[] rowLookup = new boolean[N];
 
    // Keep two arrays to tell us
    // which diagonals are occupied
    boolean slashCodeLookup[] = new boolean[2 * N - 1];
    boolean backslashCodeLookup[] = new boolean[2 * N - 1];
 
    // Initialize helper matrices
    for(int r = 0; r < N; r++)
        for(int c = 0; c < N; c++)
        {
            slashCode[r] = r + c;
            backslashCode[r] = r - c + 7;
        }
 
    if (solveNQueensUtil(board, 0, slashCode,
                         backslashCode, rowLookup,
                         slashCodeLookup,
                         backslashCodeLookup) == false)
    {
        System.out.printf("Solution does not exist");
        return false;
    }
     
    // Solution found
    printSolution(board);
    return true;
}
 
// Driver code
public static void main(String[] args)
{
    solveNQueens();
}
}
 
// This code is contributed by sujitmeshram


Python3
""" Python3 program to solve N Queen Problem
using Branch or Bound """
 
N = 8
 
""" A utility function to prsolution """
def printSolution(board):
    for i in range(N):
        for j in range(N):
            print(board[i][j], end = " ")
        print()
 
""" A Optimized function to check if
a queen can be placed on board[row][col] """
def isSafe(row, col, slashCode, backslashCode,
           rowLookup, slashCodeLookup,
                       backslashCodeLookup):
    if (slashCodeLookup[slashCode[row][col]] or
        backslashCodeLookup[backslashCode[row][col]] or
        rowLookup[row]):
        return False
    return True
 
""" A recursive utility function
   to solve N Queen problem """
def solveNQueensUtil(board, col, slashCode, backslashCode,
                     rowLookup, slashCodeLookup,
                     backslashCodeLookup):
                         
    """ base case: If all queens are
       placed then return True """
    if(col >= N):
        return True
    for i in range(N):
        if(isSafe(i, col, slashCode, backslashCode,
                  rowLookup, slashCodeLookup,
                  backslashCodeLookup)):
                     
            """ Place this queen in board[i][col] """
            board[i][col] = 1
            rowLookup[i] = True
            slashCodeLookup[slashCode[i][col]] = True
            backslashCodeLookup[backslashCode[i][col]] = True
             
            """ recur to place rest of the queens """
            if(solveNQueensUtil(board, col + 1,
                                slashCode, backslashCode,
                                rowLookup, slashCodeLookup,
                                backslashCodeLookup)):
                return True
             
            """ If placing queen in board[i][col]
            doesn't lead to a solution,then backtrack """
             
            """ Remove queen from board[i][col] """
            board[i][col] = 0
            rowLookup[i] = False
            slashCodeLookup[slashCode[i][col]] = False
            backslashCodeLookup[backslashCode[i][col]] = False
             
    """ If queen can not be place in any row in
    this colum col then return False """
    return False
 
""" This function solves the N Queen problem using
Branch or Bound. It mainly uses solveNQueensUtil()to
solve the problem. It returns False if queens
cannot be placed,otherwise return True or
prints placement of queens in the form of 1s.
Please note that there may be more than one
solutions,this function prints one of the
feasible solutions."""
def solveNQueens():
    board = [[0 for i in range(N)]
                for j in range(N)]
     
    # helper matrices
    slashCode = [[0 for i in range(N)]
                    for j in range(N)]
    backslashCode = [[0 for i in range(N)]
                        for j in range(N)]
     
    # arrays to tell us which rows are occupied
    rowLookup = [False] * N
     
    # keep two arrays to tell us
    # which diagonals are occupied
    x = 2 * N - 1
    slashCodeLookup = [False] * x
    backslashCodeLookup = [False] * x
     
    # initialize helper matrices
    for rr in range(N):
        for cc in range(N):
            slashCode[rr][cc] = rr + cc
            backslashCode[rr][cc] = rr - cc + 7
     
    if(solveNQueensUtil(board, 0, slashCode, backslashCode,
                        rowLookup, slashCodeLookup,
                        backslashCodeLookup) == False):
        print("Solution does not exist")
        return False
         
    # solution found
    printSolution(board)
    return True
 
# Driver Cde
solveNQueens()
 
# This code is contributed by SHUBHAMSINGH10


输出 :

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

表现:
在本地计算机上运行N = 32时,回溯解决方案仅需659.68秒,而分支和绑定解决方案仅需119.63秒。对于较大的N值,差异甚至会很大。

NQueen3