找到国际象棋游戏的所有组合

时间:2015-12-11 09:41:51

标签: java algorithm recursion chess

我正在制作一个程序,计算只有主教和女王的国际象棋游戏的可能解决方案的数量。用户可以输入最大数量的皇后和主教,以及国际象棋棋盘的大小。

我会将主教和棋盘上的任意位置称为组合。如果所有方格都受到攻击,则组合将计为解决方案

因此,例如,如果用户想要在3x3棋盘上计算2个皇后和1个主教的可能解决方案数量,其中两个解决方案可能是:

B--   ---
-Q-   -Q-
---   ---

如果用户选择9个皇后而不是2:

QQQ   BQQ
QQQ   QQQ
QQQ   QQQ

我已设法创建一个算法来检查组合是否是有效的解决方案。我得到的问题是找到所有可能组合的算法。所以我需要的是一种循环遍历所有可能组合的算法,并且每一种都检查组合是否是有效的解决方案。我认为递归解决方案对此最好。

1 个答案:

答案 0 :(得分:2)

可能有一些聪明的方法可以更快地解决这个问题,但我将使用递归来描绘如何使用蛮力。如果您的电路板总共有n个方块,并且您的解决方案检查算法在F(n)中运行,则此解决方案将为O(F(n)*3^n) - 换句话说,对于较大的电路板而言,速度并不快。

对于一个普通的8乘8棋盘,这可能是完全无用的,因为你遇到wheat and chessboard problem,只会更糟,因为你的解决方案检查器很昂贵而且它增长了3的幂,而不是2.如果你的碎片较少,那么一旦放下所有碎片就可以停止分支,这个问题会有所缓解。

我假设您的解决方案检查函数名为cheack_solution,它采用板上的二维数组,如果电路板是解决方案,则返回布尔值(true,否则false )。

//Just to start it off.
int count_solutions(max_queens, max_bishops, a, b) {
    int[][] board= new int[a][b];
    return recurse(board, 0, a*b, max_queens, max_bishops, 0, 0);
}

//This is where the actual work is done.
//board is the board so far, represented by a two dimensional array where
//   -1 = Queen
//    0 = Empty
//    1 = Bishop
//i is the square we are currently on, and n is the total number of board.
//max_queens and max_bishops are the maximum allowed to place.
//queens and bishops are the number placed so far.
int recurse(board, i, n, max_queens, max_bishops, queens, bishops) {
    if(i == n || (max_queens == queens && max_bishops == bishops)) {
        //If we have placed all the pieces, it is time to check if it is a solution.
        //Return one if it is, otherwise zero.
        return (int) sheck_solution(board);
    }
    //We havent placed all the pieces yet. Time to do some recursion.
    //Get the two dimensional x and y coordinates for the one dimensional coordinate i.
    x = i / board.length;
    y = i % board.length);
    //Number of solutions = the sum of number of solutions for the alternatives.
    int solutions = 0;
    //Place a queen in the current spot.
    if(queens < max_queens) {
        board[x][y] = -1;
        solutions += recurse(board, i+1, n, max_queens, max_bishops, queens + 1, bishops);
    }
    //Place a bishop in the current spot.
    if(bishops < max_bishops) {
        board[x][y] = 1;
        solutions += recurse(board, i+1, n, max_queens, max_bishops, queens, bishops + 1);
    }
    //Place nothing in the current spot.
    board[x][y] = 0;
    solutions += recurse(board, i+1, n, max_queens, max_bishops, queens, bishops);
    return solutions;
}

我没有试过这个,而且我的Java有点生疏,所以不要指望这会在第一次尝试时运行。你需要一些调试。我认为它背后的逻辑应该是好的。

编辑:根据评论中的要求,我会尝试解释为什么会这样。您可以将所有可能的板状态想象为树。首先有三个分支,每个分支对应第一个方块(皇后,主教,空)。这三个分支中的每个分支都有三个分支用于第二个方块,这三个分支中的每一个都有三个分支用于第三个方块,依此类推。

递归遍历所有这些分支,因为每次调用该函数时它都会调用自己三次。然而,两个if语句限制了traversion,以便当达到某种类型的片段的最大数量时,它不会尝试放置更多的片段。

那么为什么我们需要将三个选项中的“留空”选项放在最后?这是因为所有函数调用都使用相同的board数组。它没有被复制。因此,当函数退出时,它必须使电路板处于接收状态的相同状态。由于在调用函数时,方i中没有任何内容,因此当函数返回时,方i中应该没有任何内容。