Bootstrap

经典回溯问题:骑士游历问题

题目描述

在一个N*N 格子的棋盘上,有一只国际象棋的骑士在棋盘的左下角,骑士只能根据象棋的规则进行移动,要么横向跳动一格纵向跳动两格,要么纵向跳动一格横向跳动两格。骑士从第一个格子出发,每个格子只能访问一次,能否访问完所有的格子, 请找到一个解决方案。

题目分析

这是一道经典的回溯算法题目,这里的状态就是每一步骑士所在的坐标位置。

因为骑士可以跳动的位置总共就有8个位置,所以可以提前定义出跳动的距离数组。每一步只需要将骑士往所有方向上尝试跳动即可。

/**
 * 问题描述:在一个N*N 格子的棋盘上,有一只国际象棋的骑士在棋盘的左下角,骑士只能根据象棋的规则进行移动,
 * 要么横向跳动一格纵向跳动两格,要么纵向跳动一格横向跳动两格。
 * 骑士从第一个格子出发,每个格子只能访问一次,能否访问完所有的格子, 请找到一个解决方案。
 */

public class KnightTour {
    private int N;
    private int count;  // 棋盘上的结点数
    private int chessBoard[][];
    private int visitedNum;
    private int num;    // 解法数
    private int solution[][];   // 存储一个任意解
    private int xMove[] = new int[]{-1, 1, 2, 2, 1, -1, -2, -2};
    private int yMove[] = new int[]{2, 2, 1, -1, -2, -2, -1, 1};

    public int search(int N) {
        this.N = N;
        count = N * N;
        chessBoard = new int[N][N];
        visitedNum = 0;
        num = 0;
        solution = new int[N][N];
        backtrace(0, 0);
        if (num != 0)
            printSolution();
        return num;
    }

    private boolean backtrace(int x, int y) {
        ++visitedNum;
        chessBoard[x][y] = visitedNum;
        if (visitedNum == count) {
            ++num;
            copySolution();
            chessBoard[x][y] = 0;
            --visitedNum;
            return true;
        }

        boolean flag = false;
        for (int i = 0; i < 8; ++i) {
            int nextX = x + xMove[i];
            int nextY = y + yMove[i];
            if (isValid(nextX, nextY)) {
                if (backtrace(nextX, nextY)) flag = true;
            }
        }
        chessBoard[x][y] = 0;
        --visitedNum;
        return flag;
    }

    private boolean isValid(int x, int y) {
        if (x >= 0 && x < N && y >= 0 && y < N && chessBoard[x][y] == 0)
            return true;
        return false;
    }

    private void printSolution() {
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j) {
                System.out.print(solution[i][j] + " ");
            }
            System.out.println();
        }
    }

    private void copySolution() {
        for (int i = 0; i < N; ++i) {
            for (int j = 0; j < N; ++j)
                solution[i][j] = chessBoard[i][j];
        }
    }
}

这里我遍历了所有解法,N=6就很难跑出结果了。如果只是判断是否有解,可以优先选择下一步可跳路径少的方向进行剪枝操作,这里我就不再贴代码了。

;