验证 N 皇后问题的解决方案

Posted

技术标签:

【中文标题】验证 N 皇后问题的解决方案【英文标题】:validating a solution for N queen problem 【发布时间】:2018-12-03 07:51:46 【问题描述】:

我正在尝试编写一个函数来获得一个 MxN 大小的板,其中有一列和一行放置一个皇后。然后我想看看那个女王是否受到威胁。 通常我会拿那行并检查它是否有任何其他皇后,对于列和对角线使用 for 循环也是如此。 但是这里有一个细微的变化,棋盘上还有一些墙可以保证皇后的安全,即使同一行还有另一个皇后:

* * Q X * Q
* * X * * *
* Q X X Q *
Q * X Q Q *

其中 Q 是皇后,X 是墙,* 是空瓷砖。 位于 0,2 位置的皇后不会受到威胁,即使该行还有另一个。

整个棋盘也保存在一个二维数组中,其中皇后的 int 值为 1,墙为 -1,空白为 0。

我的想法是遍历整个行,如果某处有一个皇后,那么我应该寻找一堵墙,从那个位置到我正在看的皇后有另一个 for 循环。但是在我的女王之后还有第二部分。

我尝试考虑求和,但也不太奏效.. 有人对如何实现这一点有任何想法吗? (如果受到威胁,则返回 true;如果没有受到威胁,则返回 false;)

编辑:这是我的代码

`public static boolean isQueenThreatened(int[][] board, int row, int col)
        boolean safe=true; 
        for(int j = 0; j < col & safe ; j++)
            if(board[row][j]==1) 
                for (int i = j+1 ; i<col ; i++ ) 
                        if(board[row][i]!=-1) safe = false;
                
            
        
        for(int j = col+1; j < board[row].length & safe ; j++)
            if(board[row][j]==1) 
                for (int i = j+1 ; i<board[row].length ; i++ ) 
                    if(board[row][i]!=-1) safe = false;
                
            

        
        return safe;
    `

所以函数获取了皇后的位置(假设它是有效的),然后我想遍历那一排直到我的皇后和之后,看看是否有其他皇后,如果有我想检查是否有它们之间的墙是为了保证我的女王的安全,显然我的不起作用,因为如果它们之间只有一堵墙,它会给出错误的结果,只有一堵墙就足够了,不一定是所有的墙。

* Q * * X Q' * X Q

我用'表示我的女王,我的代码会为那个例子返回假,即使它应该是真的..然后我必须对对角线和列做同样的事情..这就是我需要的地方帮助。

【问题讨论】:

您有问题吗? 是的。如果有任何想法如何使我的想法发挥作用?如何有效地越过这一行,看看女王是否受到威胁,因为我不能仅仅决定它的右边是否有墙是安全的,因为它的左边可能有一个女王,或者根本没有女王而且没有墙,而且它仍然是安全的 如果您显示您正在尝试的代码以及哪里出了问题,您会更好地得到答案。 我已经添加了我的代码 【参考方案1】:

这是与Iterators 合作的绝佳机会。

static class Board 
    private final int width;
    private final int height;
    private final int[][] board;
    private static final int EMPTY = 0;
    private static final int WALL = -1;
    private static final int QUEEN = 1;


    public Board(int width, int height) 
        this.width = width;
        this.height = height;
        board = new int[height][width];
    

    public Board(String[] setup) 
        this(setup[0].length(), setup.length);
        for (int y = 0; y < setup.length; y++) 
            for (int x = 0; x < setup[y].length(); x++) 
                switch (setup[y].charAt(x)) 
                    case '*':
                        board[y][x] = EMPTY;
                        break;
                    case 'X':
                        board[y][x] = WALL;
                        break;
                    case 'Q':
                        board[y][x] = QUEEN;
                        break;
                
            
        
    

    public Iterator<Integer> walk(int xStart, int yStart, int dx, int dy) 
        return new Iterator<Integer>() 
            int x = xStart;
            int y = yStart;

            @Override
            public boolean hasNext() 
                return x + dx < width && y + dy < height
                        && x + dx >= 0 && y + dy >= 0;
            

            @Override
            public Integer next() 
                return board[y += dy][x += dx];
            
        ;
    

    public int get(int x, int y) 
        return board[y][x];
    


enum Direction 
    NORTH(0, -1),
    NORTH_WEST(1, -1),
    WEST(1, 0),
    SOUTH_WEST(1, 1),
    SOUTH(0, 1),
    SOUTH_EAST(-1, 1),
    EAST(-1, 0),
    NORTH_EAST(-1, -1),
    ;
    private final int dx;
    private final int dy;

    Direction(int dx, int dy) 
        this.dx = dx;
        this.dy = dy;
    


public static boolean isQueenThreatened(Board board, int row, int col) 
    for (Direction direction : Direction.values()) 
        walk: for (Iterator<Integer> attack = board.walk(col, row, direction.dx, direction.dy); attack.hasNext(); ) 
            switch (attack.next()) 
                case Board.QUEEN:
                    return true;
                case Board.WALL:
                    break walk;
            
        
    
    return false;


private void test() 
    String[] test = new String[]
            "**QX*Q",
            "**X***",
            "*QXXQ*",
            "Q*XQQ*"
    ;
    Board board = new Board(test);
    for (int y = 0; y < board.height; y++) 
        for (int x = 0; x < board.width; x++) 
            if (board.get(x, y) == Board.QUEEN) 
                System.out.println("Queen at position (" + x + "," + y + ") is " + (isQueenThreatened(board, y, x) ? "" : "NOT") + " threatened");
            
        

    

顺便说一下,(0,2) 你的女王 IS 受到(2,4) 女王的威胁。

【讨论】:

【参考方案2】:

对于给定的皇后位置,您需要遍历行、列和每个对角线。在每个方向上,您都可以遵循相同的规则:

    如果你击中另一个女王,你会受到威胁,返回true。 如果你撞到了墙,你在那个方向是安全的,继续下一个检查。 如果您击中了棋盘边缘,那么您在那个方向是安全的,请继续进行下一次检查。

public static boolean isQueenThreatened(int[][] board, int row, int col) 
    // Go over the row, to the left:
    for (int i = col - 1; i >= 0; --i) 
        int val = board[row][i];
        if (val == 1) 
            return true;
        
        if (val == -1) 
            break;
        
    

    // Same logic for:
    // - Going over the row to the right
    // - Going over the column to the top
    // - Going over the column to the bottom
    // - Going over the top left diagonal
    // - Going over the top right diagonal
    // - Going over the bottom left diagonal
    // - Going over the bottom right diagonal

    // If you reached here, it means that no early return was performed,
    // and the queen is safe
    return false;

编辑:

要回答 cmets 中的要求,您可以添加额外的 booleans 以发现撞墙的威胁,但是 TBH,我认为代码看起来会差很多:

public static boolean isQueenThreatened(int[][] board, int row, int col) 
    boolean threat = false;

    // Go over the row, to the left:
    boolean wall = false;
    for (int i = col - 1; i >= 0 && !threat && !wall; --i) 
        int val = board[row][i];
        if (val == 1) 
            threat = true;
        
        if (val == -1) 
            wall = true;
        
    

    // Go over the row, to the right.
    // Reset the wall variable, as you haven't detected a wall in this direction yet
    // The threat potentially found in the previous loop is still present
    // so if it still exists, the loop will be skipped 
    boolean wall = false;
    for (int i = col + 1; i < board[row].length && !threat && !wall; ++i) 
        int val = board[row][i];
        if (val == 1) 
            threat = true;
        
        if (val == -1) 
            wall = true;
        
    

    // Same logic for:
    // - Going over the column to the top
    // - Going over the column to the bottom
    // - Going over the top left diagonal
    // - Going over the top right diagonal
    // - Going over the bottom left diagonal
    // - Going over the bottom right diagonal

    // If you reached here, it means that no early return was performed,
    // and the queen is safe
    return threat;

【讨论】:

您知道如何做到这一点而不会中断,也不会提前返回。我的意思是我只会在函数结束时返回一次。我必须这样做,这对我来说有点困难。 @lidorportal 我已经编辑了我的答案并添加了它,但是TBH,我认为在这种情况下使用提前返回和中断会使代码更清晰。

以上是关于验证 N 皇后问题的解决方案的主要内容,如果未能解决你的问题,请参考以下文章

N皇后问题

对八皇后的补充以及自己解决2n皇后问题代码

N皇后问题12 · N-Queens

33 N皇后问题

解决 N 个皇后时的数组索引超出范围异常

为啥我的 N 个皇后问题的回溯解决方案不起作用?