如何在 N-Queen 问题中跳过重复的棋盘状态?

Posted

技术标签:

【中文标题】如何在 N-Queen 问题中跳过重复的棋盘状态?【英文标题】:How can I skip duplicate board states in N-Queen problem? 【发布时间】:2020-03-20 17:39:10 【问题描述】:

我编写了一些运行良好的代码,除了它还发现了重复的板状态。我希望对我的代码进行最小的更改,以便它只能找到唯一的板状态。我在下面粘贴我的代码:

class Solution 
    
    List<List<String>> arrangements = new ArrayList<>();
    
    public List<List<String>> solveNQueens(int n) 
        int visited[][] = new int[n][n];
        char board[][] = new char[n][n];
        solve(board,visited,0);
        return arrangements;
    
    
    public boolean solve(char board[][], int visited[][], int queens) 
        if(queens == board.length) 
            return true;
        
        for(int i = 0; i < board.length; i++) 
            for(int j = 0; j < board.length; j++) 
                if(visited[i][j] == 0) 
                    changePosition(visited,i,j,1); //set
                    board[i][j] = 'Q';
                    if(solve(board,visited,queens+1)) 
                        save(board);
                    
                    changePosition(visited,i,j,-1); // unset
                    board[i][j] = '.';
                
            
        
        return false;
    
    
    public void save(char board[][]) 
        List<String> state = new ArrayList<>();
        for(int i = 0; i < board.length; i++) 
            String row = "";
            for(int j = 0; j < board.length; j++) 
                if(board[i][j] == 'Q')
                    row += 'Q';
                else
                    row += '.';
                
            
            state.add(row);
        
        arrangements.add(state);
    
    
    public void changePosition(int visited[][], int i, int j, int val) 
        // setting the column
        for(int k = 0; k < visited.length; k++) 
            visited[k][j] += val;
        
        visited[i][j] -= val;
        //setting the row
        for(int k = 0; k < visited.length; k++) 
            visited[i][k] += val;
        
        visited[i][j] -= val;
        //setting 1 diagonal
        int a = i, b = j;
        while(a < visited.length && b < visited.length) 
            visited[a][b] += val;
            a++;
            b++;
        
        visited[i][j] -= val;
        a = i;
        b = j;
        while(a >= 0 && b >= 0) 
            visited[a][b] += val;
            a--;
            b--;
        
        visited[i][j] -= val;
        //setting 2nd diagonal
        a = i;
        b = j;
        while(a < visited.length && b >= 0) 
            visited[a][b] += val;
            a++;
            b--;
        
        visited[i][j] -= val;
        a = i;
        b = j;
        while(a >=0 && b < visited.length) 
            visited[a][b] += val;
            a--;
            b++;
        
    

函数solveNQueensList 的形式返回所有有效的棋盘状态(用String 表示的行的List 表示的棋盘状态)。提前致谢!

【问题讨论】:

【参考方案1】:

您的代码的问题在于,对于每个皇后,您都尝试了所有可能的棋盘位置。这意味着对于n 皇后,每个解决方案都将重复n! 次(n 皇后可以在解决方案中的皇后位置之间排列的次数)。

我们可以从您的程序为每个 n (reference) 找到的解决方案数量中看出这一点:

n=4:有2 解决方案,但您的程序找到48 = 2*4! = 2*24 解决方案。 n=5:有10 解决方案,但您的程序找到1200 = 10*5!=10*120 解决方案。 n=6:有4 解决方案,但您的程序找到2880 = 4*6! = 4*720 解决方案。 ...

这很容易解决:不要为每个皇后尝试所有可能的棋盘位置,而是将每个皇后限制在一个固定的列中,并且只尝试该列中的位置。这样就不会找到重复的棋盘位置了。

在您的solve 方法中,删除内部for 循环,而是将j 坐标设置为女王的编号(意味着女王0 将在第0 列,女王1 将在第1 列,等等):

  public boolean solve(char board[][], int visited[][], int queens) 
    if (queens == board.length) 
      return true;
    
    for (int i = 0; i < board.length; i++) 
      int j = queens;
      ...

【讨论】:

以上是关于如何在 N-Queen 问题中跳过重复的棋盘状态?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据某些条件在 MSSQL 游标中跳过一行(迭代)?

在 Vim 中跳过撤消步骤

在数组中跳过mysql数据库中的值[重复]

如何从mysql中的表中跳过行

如何在函数调用中跳过可选参数?

使用 CSV 文件在循环中跳过第一行(字段)? [复制]