12 dominating knights puzzle (backtracking)

前端 未结 5 1973
感动是毒
感动是毒 2021-02-07 05:47

I\'ve been searching for hours and haven\'t found a fully working solution for this kind of puzzle yet. So I followed similar problem with bishops.

What I need to do is

5条回答
  •  鱼传尺愫
    2021-02-07 06:26

    As pointed out by @gnasher729 trying to place all the 12 knights would be very inefficient, so we can try to place 6 knights on white blocks instead, but using this approach we can only attack a maximum of 30 black blocks out of 32.

    So from the above we can take 2 approaches :

    1) We can fix 2 knights on the remaining 2 black blocks, and then try to place the remaining 4 knights on the remaining 30 black blocks, notice now we only need to attack the remaining 26 white blocks.

    @gnasher729 said that we could mirror the solution, but i was not able to come up with the logic of fixing 2 places and then finding the mirror, because only 30 blocks were being attacked, had the no of knights been 14, then all the 32 blocks would have been attacked, and finding a mirror maybe would have been possible.

    2) The second would be to brute force the remaining 6 knights whenever we find a solution for the first 6 knights that attacked more than 26 black blocks, which i implemented but that was still not finding a solution.

    So as @n.m. said we can try to find solutions from the center to reduce the search space, so i tried to find solution by placing the knights in the 6 X 6 center square, and further only looked for solutions when 30 out of the 32 black blocks were being attacked instead of 26, and finally was able to find 2 solutions to the problem which were both symmetric, there might be more solutions available to the problem with a better approach.

    Code in c++ :

    #include 
    #include 
    
    using namespace std;
    
    #define N 8
    
    int board[N][N], mark[N][N];
    
    void placeOnBoard(int i, int j){
        int count = 0;
        if(mark[i][j] == 0) mark[i][j] = 1;
        if(i+2 < N && j+1 < N && mark[i+2][j+1] == 0) mark[i+2][j+1] = 1;
        if(i+2 < N && j-1 >= 0 && mark[i+2][j-1] == 0) mark[i+2][j-1] = 1;
        if(i-2 >= 0 && j+1 < N && mark[i-2][j+1] == 0) mark[i-2][j+1] = 1;
        if(i-2 >= 0 && j-1 >= 0 && mark[i-2][j-1] == 0) mark[i-2][j-1] = 1;
    
        if(j+2 < N && i+1 < N && mark[i+1][j+2] == 0) mark[i+1][j+2] = 1;
        if(j+2 < N && i-1 >= 0 && mark[i-1][j+2] == 0) mark[i-1][j+2] = 1;
        if(j-2 >= 0 && i+1 < N && mark[i+1][j-2] == 0) mark[i+1][j-2] = 1;
        if(j-2 >= 0 && i-1 >= 0 && mark[i-1][j-2] == 0) mark[i-1][j-2] = 1;
    }
    
    void printBoard(){
        for(int i = 0;i < N;i++){
            for(int j = 0;j < N;j++){
                if(board[i][j] != 0) cout << "K ";
                else cout << board[i][j] << " ";
            }
            cout << endl;
        }
        cout << endl;
    }
    
    void backtrackBlack(int knightNum, int currX, int currY){
        if(knightNum == 7){
            int count = 0;
            for(int i = 0;i < N;i++) for(int j = 0;j < N;j++) mark[i][j] = 0;
            for(int i = 0;i < N;i++){
                for(int j = 0;j < N;j++) if(board[i][j] != 0) placeOnBoard(i, j);
            }
            for(int i = 0;i < N;i++){
                for(int j = 0;j < N;j++) if(mark[i][j] != 0) count++;
            }
            if(count == 64) printBoard();
            return;
        }
        if(currX == N-1 && currY == N) return;
        int newX, newY;     //new place in the board to move to
        if(currY == N) newY = 0,newX = currX + 1;
        else newY = currY + 1,newX = currX;
    
        //do not place the current knight at (currX, currY)
        backtrackBlack(knightNum, newX, newY);
        //try to place the current knight at (currX, currY)
        if((currX + currY) % 2 == 1 && currX > 0 && currX < N-1 && currY > 0 && currY < N-1){
            board[currX][currY] = knightNum;
            backtrackBlack(knightNum+1, newX, newY);
            board[currX][currY] = 0;
        }
    }
    
    void backtrackWhite(int knightNum, int currX, int currY){
        if(knightNum == 7){
            int count = 0;
            for(int i = 0;i < N;i++) for(int j = 0;j < N;j++) mark[i][j] = 0;
            for(int i = 0;i < N;i++){
                for(int j = 0;j < N;j++) if(board[i][j] != 0) placeOnBoard(i, j);
            }
            for(int i = 0;i < N;i++){
                for(int j = 0;j < N;j++) if(mark[i][j] != 0) count++;
            }
            if(count >= 32){
                backtrackBlack(1, 0, 0);
                //printBoard();
            }
            return;
        }
        if(currX == N-1 && currY == N) return;
        int newX, newY;     //new place in the board to move to
        if(currY == N) newY = 0,newX = currX + 1;
        else newY = currY + 1,newX = currX;
    
        //do not place the current knight at (currX, currY)
        backtrackWhite(knightNum, newX, newY);
        //try to place the current knight at (currX, currY)
        if((currX + currY) % 2 == 0 && currX > 0 && currX < N-1 && currY > 0 && currY < N-1){
            board[currX][currY] = knightNum;
            backtrackWhite(knightNum+1, newX, newY);
            board[currX][currY] = 0;
        }
    }
    
    int main(){
        time_t t = clock();
        backtrackWhite(1, 0, 0);
        t = clock() - t;
            double time_taken = ((double)t)/CLOCKS_PER_SEC;
            cout << "Time Taken : " << time_taken<< endl;
        return 0;
    }
    

    It only finds 2 solution in about 89 seconds.

    Output :

    0 0 0 0 0 0 0 0
    0 0 K 0 0 0 0 0
    0 0 K K 0 K K 0
    0 0 0 0 0 K 0 0
    0 0 K 0 0 0 0 0
    0 K K 0 K K 0 0
    0 0 0 0 0 K 0 0
    0 0 0 0 0 0 0 0
    
    0 0 0 0 0 0 0 0
    0 0 0 0 0 K 0 0
    0 K K 0 K K 0 0
    0 0 K 0 0 0 0 0
    0 0 0 0 0 K 0 0
    0 0 K K 0 K K 0
    0 0 K 0 0 0 0 0
    0 0 0 0 0 0 0 0
    
    Time Taken : 89.2418
    

提交回复
热议问题