我正在制作一个程序,计算只有主教和女王的国际象棋游戏的可能解决方案的数量。用户可以输入最大数量的皇后和主教,以及国际象棋棋盘的大小。
我会将主教和棋盘上的任意位置称为组合。如果所有方格都受到攻击,则组合将计为解决方案。
因此,例如,如果用户想要在3x3棋盘上计算2个皇后和1个主教的可能解决方案数量,其中两个解决方案可能是:
B-- ---
-Q- -Q-
--- ---
如果用户选择9个皇后而不是2:
QQQ BQQ
QQQ QQQ
QQQ QQQ
我已设法创建一个算法来检查组合是否是有效的解决方案。我得到的问题是找到所有可能组合的算法。所以我需要的是一种循环遍历所有可能组合的算法,并且每一种都检查组合是否是有效的解决方案。我认为递归解决方案对此最好。
答案 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
中应该没有任何内容。