8位主教占据整个董事会[Backtracking]

时间:2016-03-29 09:10:22

标签: java algorithm backtracking

我有一个任务是编写一个程序,在国际象棋棋盘上设置8位主教占据整个董事会。它应该在找到第一个解决方案时结束并打印出所有内容。这是我用Java编写的代码,我很难用回溯来完成它(这个地方在代码中被注释)。

/*
 * 0 - not occupied square
 * 1 - bishop standing square
 * 2 - occupied square (diagonal)
 */
public class BishopsBT {
public int [][] solution;
final int N = 8; // number of squares in column and row (chess board)
final int solved = 120; //Sum of 1's and 2's in case of all occupied board
int sum; //current sum of board

public BishopsBT(){
    solution = new int [N][N] ;
}

public void solve() {
    if(placeBishops(0)){
        //print the result
        clear(); // clears all 2's
        for (int i = 0; i < N; i++) {
            for (int j = 0; j < N; j++) {
                System.out.print(" " + solution[i][j]);
            }
            System.out.println();
        }
    } else{
        System.out.println("NO SOLUTION EXISTS");
    }
}

public boolean placeBishops (int bishop){

    for (int row = 0; row < N; row++) {
        // check if bishop can be placed
        if (canPlace(solution, row, bishop)) {
            // place the bishop
            solution[row][bishop] = 1;  
            }
        }

    if (allSpaceOccupied()) {
        return true;
    } else {
        // SOME BACKTRACKING CODE HERE
        return false;
    }

    }

// check if bishop can be placed at matrix[row][column]
public boolean canPlace(int[][] matrix, int row, int column) {

    // we need to check all diagonals
    // whether no bishop is standing there

    for (int i = row, j = column; i >= 0 && j >= 0; i--, j--) {
        if (matrix[i][j] == 1) {
            return false;
        }
    }

    for (int i = row, j = column; i >= 0 && j < matrix.length; i--, j++) {
        if (matrix[i][j] == 1) {
            return false;
        }
    }

    for (int i = row, j = column; i < matrix.length && j >= 0; i++, j--) {
        if (matrix[i][j] == 1) {
            return false;
        }
    }

    for (int i = row, j = column; i < matrix.length && j < matrix.length; i++, j++) {
        if (matrix[i][j] == 1) {
            return false;
        }
    }
    // if we are here that means we are safe to place Bishop
    return true;
}

public boolean allSpaceOccupied() {

    // clears previously occupied space
    clear();

    // occupies new space
    for (int i = 0; i < solution.length; i++) {
        for ( int j = 0; j < solution.length; j++) {
    if (solution[i][j] == 1) diagonalOccupy(i,j);
        }
    }
    sum = 0;
    // counts sum of occupied space
    for (int i = 0; i < solution.length; i++) {
        for ( int j = 0; j < solution.length; j++) {
    sum += solution [i][j];
        }
    }

    if (sum == solved) return true;
    // else
    return false;
}

public void diagonalOccupy(int row, int column) {
    // writes 2 in each bishop's occupied square
    for (int i = row, j = column; i >= 0 && j >= 0; i--, j--) {
        if (solution[i][j] == 0) {
            solution[i][j] = 2;
        }
    }

    for (int i = row, j = column; i >= 0 && j < solution.length; i--, j++) {
        if (solution[i][j] == 0) {
            solution[i][j] = 2;
        }
    }

    for (int i = row, j = column; i < solution.length && j >= 0; i++, j--) {
        if (solution[i][j] == 0) {
            solution[i][j] = 2;
        }
    }

    for (int i = row, j = column; i < solution.length && j < solution.length; i++, j++) {
        if (solution[i][j] == 0) {
            solution[i][j] = 2;
        }
    }

}
 // clears all 2's on the board
public void clear() {
    for (int i = 0; i < solution.length; i++) {
        for ( int j = 0; j < solution.length; j++) {
            if (solution[i][j] == 2) solution[i][j] = 0;
        }
    }
}

public static void main(String[] args) {
    BishopsBT q = new BishopsBT();
    q.solve();
}
}

事情是,目前我的程序将主教放在第一列,这种布局不占用所有空间。当然,我可以简单地将所有内容放在第三列,问题就解决了。但是,我必须使用回溯并且不知道如何。如果您有任何想法或提示,我会很高兴听到它们。

2 个答案:

答案 0 :(得分:2)

您的解决方案假定所有主教必须放在不同的行中。并非所有解决方案都适用。 (有一个解决方案,所有主教都在第三或第四列。你不是在寻找所有的解决方案,但如果你是这样的话,你会错过这个假设的许多解决方案。)

你也不需要canPlace检查:主教不能相互威胁是没有限制的。 (这可能是加速搜索的有效技术,但同样,当你应用它时,你会错过一些解决方案。如果你想使用它,就没有必要检查已经放置过的主教的所有对角线单元格;它足以检查当前单元格是否已被标记为“已占用”或受到威胁。)

如果你打算使用蛮力方法进行回溯,你可以测试所有可能的主教组合。这是C(64,8)或4,426,165,368种组合。

你可以大幅削减可能性,但不要假设主教必须在不同的行。相反,请注意您的解决方案包含两个独立的解决方案。白色广场上的主教只能威胁到白色方块,黑色方块上的主教只能威胁到黑色方块。因此,找到一个解决方案,在板上放置四个主教,威胁所有的白色方块。然后

(如果您想找到所有解决方案,请找到所有 k 子解决方案,并将它们合并到 k ²完整解决方案中。)

这种案件分离减少了对C(32,8)或35,960进行测试的可能安排。你的策略只考虑每行有一个主教的配置,检查8 ^ 8(约1600万)的可能性。它错过了一些解决方案,并检查了多个配置,其中没有四个主教在白色方块上,四个在黑色方块上。

回溯的原则在另一个答案中给出。如果您将32个白色方块标记为:

01  02  03  04
  05  06  07  08
09  10  11  12  
  13  14  15  16
17  18  19  20
  21  22  23  24
25  26  27  28
  29  30  31  32

你可以使用像这样的递归方法(在伪Java中):

bool place(int i, int start) {
    if (i == 8) {
        if (allOccupied()) {
           print();
           return true;
        }
    } else {
        for (int j = start, j < 32; j++) {
            int row = j / 4;
            int col = 2 * (j % 4) + row % 2;

            // add bishop at (col, row)
            // save occupancy matrix
            // add threat by (col, row) to matrix

            if (place(i + 1, j + 1)) return true;

            // revert matrix to saved matrix
            // remove bishop from (col, row)
       }
    }

    return false;
}

并以

开头
place(0, 0);

答案 1 :(得分:0)

你应该这样做:

public boolean placeBishops (int bishop){
    if(bishop == 8){
        if(allSpaceOccupied()){
            //Print the board here, i.e found the solution
            //also check the indexing of the bishop,
            //i have assumed that they start from 0
            return true;
        }else{
            return false;
        }
    }
    for (int row = 0; row < N; row++) {
        // check if bishop can be placed
        if (canPlace(solution, row, bishop)) {
            // place the bishop
            solution[row][bishop] = 1;
            boolean found = placeBishops(bishop+1);
            if(found == true) return true;
            solution[row][bishop] = 0;
        }
    }
    return false;
}

我们可以检查一个地方是否适合特定的主教,并相应地增加主教数量,如果我们没有找到解决方案沿着那条路走下去,我们重置当前主教索引的solution array以及该主教的相应行,以便我们可以寻找另一种可能的解决方案。