在Java/C++中使用回溯法解决N皇后问题

如果你喜欢下棋,你会喜欢学习N皇后问题。这是一个很好的问题,可以帮助你理解回溯算法。

什么是回溯法?

在回溯法中,我们从众多可行的移动中选择一个开始。然后我们尝试解决问题。

如果我们能够通过选择的移动来解决问题,那么我们将打印解决方案。否则,我们将回溯并选择其他移动,尝试解决问题。

如果所有的解决方案都不起作用,我们宣称这个问题没有解决方案。

N皇后问题是什么?

How can N queens be placed on an NxN chessboard so that no two of them attack each other?

这个问题在N=4和N=8时经常出现。

让我们来看一个N=4的例子。

在解决问题之前,你需要了解国际象棋中皇后的移动方式。 (Zài jiějué wèntí zhīqián, nǐ xūyào liǎojiě guójì xiàngqí zhōng huáng hòu de yídòng fāngshì.)

一位皇后可以向任意方向移动任意数量的步骤。唯一的限制是,它在移动过程中不能改变方向。

通过观察皇后的移动可以清楚地看出一件事,即没有两个皇后可以位于同一行或同一列。

这使得我们只能在每一行和每一列上放置一个皇后。

当N等于4时,解决方案如下:

Queen Solution

但是我们如何取得这个安排?

N-皇后问题的解决方案

我们解决这个问题的方式是在一个位置上放置一枚皇后,并试图排除其被攻击的可能性。我们在每一行/每一列都放置一个皇后。

如果我们发现女王在已选的位置受到攻击,我们尝试下一个位置。

如果一個皇后被攻擊在一排的所有位置上,我們將回溯並改變先前位置放置的皇后的位置。

我们不断重复放置皇后并进行回溯的过程,直到所有N个皇后都成功地放置完成。

以下是逐步回溯的步骤展示:

Queen 1

红色十字标志着在攻击中的位置,这些位置受到皇后的威胁。每当我们遇到一种情况,所有行中的位置都受到攻击,但我们又有一个要放置的皇后时,我们就会回溯。

这并不是问题的唯一解决方案。如果你按照顺时针的方式将每个皇后向前移动一步,就会得到另一个解决方案。

Queen 8

在这个例子中,我们按照行来摆放皇后,我们也可以按照列进行相同的操作。那样的话,每个皇后将属于一列。

使用C++和Java实现N皇后问题

在C++中实现N皇后问题。

#define N 4 
#include <stdbool.h> 
#include <stdio.h> 
//function to print the solution
void printSolution(int board[N][N]) 
{ 
    for (int i = 0; i < N; i++) { 
        for (int j = 0; j < N; j++) 
            printf(" %d ", board[i][j]); 
        printf("\n"); 
    } 
} 

 // function to check whether the position is safe or not 
bool isSafe(int board[N][N], int row, int col) 
{ 
    int i, j; 
    for (i = 0; i < col; i++) 
        if (board[row][i]) 
            return false; 

    for (i = row, j = col; i >= 0 && j >= 0; i--, j--) 
        if (board[i][j]) 
            return false; 
    for (i = row, j = col; j >= 0 && i < N; i++, j--) 
        if (board[i][j]) 
            return false; 
  
    return true; 
} 

// The function that solves the problem using backtracking 
bool solveNQUtil(int board[N][N], int col) 
{ 
    if (col >= N) 
        return true; 
  
   
    for (int i = 0; i < N; i++) { 
       //if it is safe to place the queen at position i,col -> place it
        if (isSafe(board, i, col)) { 
         
            board[i][col] = 1; 
  
         
            if (solveNQUtil(board, col + 1)) 
                return true; 

  //backtrack if the above condition is false
            board[i][col] = 0; // BACKTRACK 
        } 
    } 
  
   
    return false; 
} 

// driver program to test above function 
int main() 
{ 
     int board[N][N] = { { 0, 0, 0, 0 }, 
                        { 0, 0, 0, 0 }, 
                        { 0, 0, 0, 0 }, 
                        { 0, 0, 0, 0 } }; 
  
    if (solveNQUtil(board, 0) == false) { 
        printf("Solution does not exist"); 
        return 0; 
    } 
  
    printSolution(board); 
    return true; 
    return 0; 
} 

在Java中实现N皇后问题。

package com.JournalDev;
public class Main {
    static final int N = 4;

   // print the final solution matrix 
    static void printSolution(int board[][])
    {
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++)
                System.out.print(" " + board[i][j]
                        + " ");
            System.out.println();
        }
    }

    // function to check whether the position is safe or not 
    static boolean isSafe(int board[][], int row, int col)
    {
        int i, j;
        for (i = 0; i < col; i++)
            if (board[row][i] == 1)
                return false;

       
        for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
            if (board[i][j] == 1)
                return false;
            
        for (i = row, j = col; j >= 0 && i < N; i++, j--)
            if (board[i][j] == 1)
                return false;

        return true;
    }

    // The function that solves the problem using backtracking 
    public static boolean solveNQueen(int board[][], int col)
    {
        if (col >= N)
            return true;

        for (int i = 0; i < N; i++) {
            //if it is safe to place the queen at position i,col -> place it
            if (isSafe(board, i, col)) {
                board[i][col] = 1;

                if (solveNQueen(board, col + 1))
                    return true;

                //backtrack if the above condition is false
                board[i][col] = 0;
            }
        }
        return false;
    }

    public static void main(String args[])
    {
        int board[][] = { { 0, 0, 0, 0 },
                { 0, 0, 0, 0 },
                { 0, 0, 0, 0 },
                { 0, 0, 0, 0 } };

        if (!solveNQueen(board, 0)) {
            System.out.print("Solution does not exist");
            return;
        }

        printSolution(board);
       
    }
} 

Output : 
0  0  1  0 
1  0  0  0 
0  0  0  1 
0  1  0  0 
Queen Output

当N=8时,输出结果为:

 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
Queen 8

理解代码的实现

为了检查一个位置是否受到攻击,我们创建了一个名为isSafe的函数。

如果位置安全且没有受到攻击,则该函数返回true。

 static boolean isSafe(int board[][], int row, int col)
    {
        int i, j;
        for (i = 0; i < col; i++)
            if (board[row][i] == 1)
                return false;

        for (i = row, j = col; i >= 0 && j >= 0; i--, j--)
            if (board[i][j] == 1)
                return false;
            
        for (i = row, j = col; j >= 0 && i < N; i++, j--)
            if (board[i][j] == 1)
                return false;

        return true;
    }

第一个循环通过列进行检查,第二个和第三个循环通过两个对角线进行检查。

以下代码负责将皇后放置在它们的位置并进行回溯。为了标记皇后的位置,我们将该单元格在矩阵中设置为1。在放置皇后之前,我们调用isSafe函数来确保位置安全。

public static boolean solveNQueen(int board[][], int col)
    {
        if (col >= N)
            return true;

        for (int i = 0; i < N; i++) {
            //if it is safe to place the queen at position i,col -> place it
            if (isSafe(board, i, col)) {
                board[i][col] = 1;

                if (solveNQueen(board, col + 1))
                    return true;

                //backtrack if the above condition is false
                board[i][col] = 0;
            }
        }
        return false;
    }

递归调用将棋盘传入并将列设置为col+1。如果递归调用返回false,则通过将该位置重新设置为0来进行回溯。

结论

这是使用回溯法解决N皇后问题的方法。要了解更多关于回溯法的知识,可以尝试解决数独问题。

发表回复 0

Your email address will not be published. Required fields are marked *