【问题标题】:Knight's tour recursive approach Java骑士之旅递归方法Java
【发布时间】:2016-07-11 07:20:01
【问题描述】:

我正在尝试递归地实现骑士之旅。下面是我的代码。 为简单起见,我选择了 5x5 板。我的目标是打印出正确的骑士位置,并可能在打印语句的同时显示回溯。

class Knight
{
public boolean[][] board = new boolean[5][5];

public final int SQUARES = 5*5-1;

public Knight()
{

    for(int i=0;i<5;i++)
        for(int j=0;j<5;j++)
            board[i][j] = false;

}

public boolean tour(int x, int y, int visitedX, int visitedY,int n)// x,y coords, just visited x,y and counter n
{

    if(x>=5||y>=5||x<0||y<0){ //out of bounds
        System.out.println("Visited "+x+", "+y+" Out of bounds");
        System.out.println("Back to "+visitedX+", "+visitedY);
        return false;
    }
    else{
        if(board[x][y] == true)
        {return false;}
        if(board[x][y]!=true){
            boolean result = false;
            board[x][y]=true;
            System.out.println("Visited "+x+", "+y);
            result = tour(x+2,y+1,x,y,n+1); 
            result = tour(x+2,y-1,x,y,n+1); 
            result = tour(x+1,y-2,x,y,n+1); 
            result = tour(x-1,y-2,x,y,n+1); 
            result = tour(x+1,y+2,x,y,n+1); 
            result = tour(x-1,y+2,x,y,n+1);
            result = tour(x-2,y-1,x,y,n+1); 
            result = tour(x-2,y+1,x,y,n+1); 

        }
    }
    return false;
}
}
public class KnightTApp {

public static void main(String[] args) {
    Knight k = new Knight();
    k.tour(0,0,0,0,0);

}

}

部分打印输出

Visited 4, 0
Visited 6, 1 Out of bounds
Back to 4, 0
Visited 6, -1 Out of bounds
Back to 4, 0
Visited 5, -2 Out of bounds
Back to 4, 0
Visited 3, -2 Out of bounds
Back to 4, 0
Visited 5, 2 Out of bounds
Back to 4, 0
Visited 2, -1 Out of bounds
Back to 4, 0
Visited 2, 0

我的问题是在这个过程的中间,我在索引 4,0 处遇到了一个死胡同(不是所有的方块都被覆盖,但我无法进行合法的移动)。而且由于我没有包含任何 回溯 行。它只是跳转到索引 2,0,这甚至不是合法的骑士移动。

我的问题是如何用 if 语句表达死胡同?我想我应该将递归设置为某种布尔值并从那里开始,但我不知道该怎么做。

并且还使用递归与使用 DFS(深度优先搜索)相同,因为它们基本上具有相同的想法?

提前谢谢你。

【问题讨论】:

  • 从技术上讲,由于您使用的是递归搜索,因此该函数的任何返回都将是“回溯”。但是由于您从不从tour() 返回除false 以外的任何内容,因此您无法在调用树上发出成功/失败的信号。你只会说“失败”。
  • 我不确定如何在代码中表达这种情况。我知道获胜条件是所有方格都被访问,但我该如何表达呢?
  • 我想一旦我弄清楚如何表达死胡同的情况,我会将counter部分设置为“true”的结果
  • 您可以跟踪游戏板。对于每个 tour() 调用,您都会在棋盘中记录该移动,当棋盘填满时,您开始返回 true 备份树。如果你走到了死胡同并且板子没有满,那么这是一个失败的分支,你返回 false。
  • 没错!但我怎么知道这是一个死胡同?我的意思是我当然可以从打印输出中看出,但我希望代码知道这是一个死胡同并从那里重新跟踪..

标签: java recursion knights-tour


【解决方案1】:

您需要弄清楚您是否正在尝试识别所有可能的游览 或者只是为了找到一个有效的游览。

要获得完整的回溯,您需要在游览结束时再次将棋盘字段标记为空闲。

其次,您需要检查tour() 上每个递归调用的结果。 如果为真,你找到了一个有效的星座,你也可以返回真。 否则尝试下一个可能的举动。如果没有向左移动,则返回 false。

最后你错过了一个适当的终止条件:“什么是成功?” 在您的情况下,如果您有 5x5 成功移动,则所有字段都已访问,您可能只返回 true 表示成功。

您可能还想跟踪移动的顺序,以便有机会输出成功的游览。

上面的目标是确定一个成功的旅行。
通过跟踪移动序列,您可以跟踪所有成功的游览,而不是仅在最深的递归检查已知游览时返回 true,如果是新游览则返回 true。

为了参考您的代码的修改版本,添加“查找任何”部分:

class Knight {
    public final static int SIZE = 5;

    public boolean[][] board = new boolean[SIZE][SIZE];
    public String[] moves = new String[SIZE * SIZE];

    public Knight() {

        for (int i = 0; i < 5; i++ ) {
            for (int j = 0; j < 5; j++ ) {
                board[i][j] = false;
            }
        }

    }

    public boolean tour(final int x, final int y, final int n)
    {
        if (n >= SIZE * SIZE) {
            return true;
        } else if (x >= 5 || y >= 5 || x < 0 || y < 0) { // out of bounds
            return false;
        } else if (board[x][y]) {
            return false;
        } else {
            // System.out.println("Checking " + n + ": " + x + "," + y);
            board[x][y] = true;
            moves[n] = x + "-" + y;
            if (tour(x + 2, y + 1, n + 1)) {
                return true;
            }
            if (tour(x + 2, y - 1, n + 1)) {
                return true;
            }
            if (tour(x + 1, y - 2, n + 1)) {
                return true;
            }
            if (tour(x - 1, y - 2, n + 1)) {
                return true;
            }
            if (tour(x + 1, y + 2, n + 1)) {
                return true;
            }
            if (tour(x - 1, y + 2, n + 1)) {
                return true;
            }
            if (tour(x - 2, y - 1, n + 1)) {
                return true;
            }
            if (tour(x - 2, y + 1, n + 1)) {
                return true;
            }
            board[x][y] = false;
            moves[n] = null;
            return false;
        }
    }

    public static void main(final String[] args) {
        final Knight k = new Knight();

        for (int i = 0; i < SIZE; i++ ) {
            System.out.println("Starting at " + i + " 0");

            if (k.tour(i, 0, 0)) {
                k.printTour(true);
                break;
            }
            k.printTour(true);
        }
    }

    /**
     * @param result
     */
    private void printTour(final boolean result) {
        System.out.println("Found tour: " + result);
        int i = 0;
        if (result) {
            for (final String m : moves) {
                System.out.println("M-" + i + ": " + moves[i]);
                i++ ;
            }
        }
    }
}

【讨论】:

  • 您好,感谢您的解决方案,只是一个简单的问题。 int 变量 max 到底是做什么的?我猜它被用作某种计数器,但 SIZE 变量不是也用于控制移动吗?
  • max 可以被淘汰。刚刚添加为针对一个错误的验证。所以,我的懒惰。
【解决方案2】:

您可能希望使用 Stack 数据结构,并在每次移动时评估所有可能的有效移动,并将每个移动推入堆栈。当您进行有效移动时,将其从堆栈中弹出。

当您的评估方法返回 0 个可能的移动时,就会出现死胡同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多