3

代码对我来说似乎很好,但是输出太短了,当它应该是肯定的时候答案是否定的(从 a,0 开始,骑士应该能够游览整个棋盘)

顺便说一句,我的positionsVisted数组是 [9][9] 的原因是因为我希望值是 1-8,以匹配输出。

public class KnightChessRiddle {
static // given a chess board and a 0,0 starting point, can a knight pass through
// all the the squares without passing
// a square twice

int[][] positionsVisited = new int[9][9];
static int positionX = 1;
static int positionY = 1;
boolean stop = false;
static boolean continUe = false;
static int moveCounter = -1;

public static void main(String[] args) {
    if (recursive(1, 1, 0)) {
        System.out.println("yes");
    }
    else System.out.println("no");
}

public static boolean recursive(int x, int y, int moveType){


    if (x>8||x<=0||y>8||y<=0) return false;
    if (positionsVisited[x][y]==1) {
        return false;
    }
    positionX = x;
    positionY = y;
    positionsVisited[positionX][positionY]++;

    char c;
    c='a';
    switch (positionX) {
    case 1:
        c='a';
        break;
    case 2:
        c='b';
        break;
    case 3:
        c='c';
        break;
    case 4:
        c='d';
        break;
    case 5:
        c='e';
        break;
    case 6:
        c='f';
        break;
    case 7:
        c='g';
        break;
    case 8:
        c='h';
        break;

    default: 
        break;
    }
    moveCounter++;
    System.out.println("doing move "+moveType+" move count: "+moveCounter);
    System.out.println("Knight is in "+ c +","+positionY);

    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    if (recursive(positionX+2, positionY+1, 1)) {
        return true;
    }
    else if (recursive(positionX+1, positionY+2, 2) ) {
        return true;
    }
    else if (recursive(positionX+2, positionY-1, 3) ) {
        return true;
    }
    else if (recursive(positionX+1, positionY-2, 4)) {
        return true;
    }
    else if (recursive(positionX-2, positionY+1, 5)) {
        return true;
    }
    else if (recursive(positionX-1, positionY+2, 6)) {
        return true;
    }
    else if (recursive(positionX-2, positionY-1, 7)) {
        return true;
    }
    else if (recursive(positionX-1, positionY-2, 8)) {
        return true;
    }
    else return false;
}

这是程序的输出:

doing move 0 move count: 0
Knight is in a,1
doing move 1 move count: 1
Knight is in c,2
doing move 1 move count: 2
Knight is in e,3
doing move 1 move count: 3
Knight is in g,4
doing move 2 move count: 4
Knight is in h,6
doing move 5 move count: 5
Knight is in f,7
doing move 1 move count: 6
Knight is in h,8
doing move 8 move count: 7
Knight is in g,6
doing move 4 move count: 8
Knight is in h,4
doing move 5 move count: 9
Knight is in f,5
doing move 2 move count: 10
Knight is in g,7
doing move 4 move count: 11
Knight is in h,5
doing move 5 move count: 12
Knight is in f,6
doing move 1 move count: 13
Knight is in h,7
doing move 5 move count: 14
Knight is in f,8
doing move 7 move count: 15
Knight is in d,7
doing move 4 move count: 16
Knight is in e,5
doing move 4 move count: 17
Knight is in f,3
doing move 2 move count: 18
Knight is in g,5
doing move 4 move count: 19
Knight is in h,3
doing move 5 move count: 20
Knight is in f,4
doing move 4 move count: 21
Knight is in g,2
doing move 7 move count: 22
Knight is in e,1
doing move 6 move count: 23
Knight is in d,3
doing move 3 move count: 24
Knight is in f,2
doing move 3 move count: 25
Knight is in h,1
doing move 6 move count: 26
Knight is in g,3
doing move 5 move count: 27
Knight is in e,4
doing move 5 move count: 28
Knight is in c,5
doing move 1 move count: 29
Knight is in e,6
doing move 5 move count: 30
Knight is in c,7
doing move 1 move count: 31
Knight is in e,8
doing move 8 move count: 32
Knight is in d,6
doing move 5 move count: 33
Knight is in b,7
doing move 1 move count: 34
Knight is in d,8
doing move 8 move count: 35
Knight is in c,6
doing move 1 move count: 36
Knight is in e,7
doing move 1 move count: 37
Knight is in g,8
no
4

2 回答 2

4

您实现的算法如下:

将可能的骑士移动从一个正方形排序如下:

在每次移动中,选择仍然允许的最小编号移动。

(A) 如果你覆盖了所有的方格,返回true

(B) 如果你不能再移动,返回false

您的代码有两个问题。第一个,已经指出,是你错过了支票(A)。第二个更严重的问题是这个算法不起作用。实际上,您最终会得到以下结果:

在这张图片中,黑骑士代表起点和终点方格,而白骑士代表所有其他方格。如果您遵循您的算法,您最终会进入一个无法到达尚未覆盖的任何其他方格的方格。这并不意味着你不能在棋盘上进行骑士之旅,只是你的算法不起作用。


如果这确实是您想要实现的算法,那么没有理由使用递归,因为for循环也可以工作。recursive当我们当前所在的方格存在有效的骑士移动时,您的函数将返回 true。这实际上不是递归算法有两个原因:

  1. recursive函数不是幂等的——它有一个副作用,就是填充positionsVisited数组的一个正方形。
  2. recursive函数调用全局变量- positionsVisited(我说的是“全局变量”,而不是“私有字段”,因为您编写的内容本质上是程序代码)。

相反,该函数recursive应该告诉你一条更一般的信息:给定棋盘上的一个特定方格,以及我们不允许访问的一组特定方格,是否有骑士游走剩余的方格?(当然,使用正方形 a1 和已访问位置的空数组调用该函数将告诉您是否存在骑士之旅。)该函数还可以返回一个字符串,该字符串将告诉您骑士之旅是什么。

递归函数的结构应该如下所示:

private boolean isKnightsTour(Square currentSquare,
                              int[9][9] visitedSquares,
                              KnightsTour tour)
{
  // Append the current square to the array of visited squares.
  int[9][9] newVisitedSquares = visitedSquares;
  newVisitedSquares[currentSquare.getX()][currentSquare.getY()] = 1;

  // If we have visited all the squares, there is a knight's tour.  
  // Add some code here to check for that.
  if (allSquaresVisited()) {
    tour = new KnightsTour(currentSquare);
    return true;
  }

  // Test all squares a knight's move away.  If you get a knight's tour, 
  // append the current square to the start and return that.  
  KnightsTour newTour;
  if (isKnightsTour(currentSquare.doMove1(), newVisitedSquares, newTour) {
    newTour.appendStart(currentSquare);
    tour = newTour;
    return true;
  }

  // Repeat for the other knight's moves.

  else {
    tour = null;
    return false;
  }
}

或者,用一句话来说:

递归地检查骑士离开当前方格的所有方格,传入新方格和通过添加当前方格形成的新访问方格数组。如果其中一个方格有骑士之旅,请将当前方格附加到它的开头以从当前方格获得骑士之旅。

而不是你写的是:

通过从一个正方形开始并(相当随意地)在每一步选择一个合法的骑士移动来递归地建立一个骑士之旅。如果我们到了不能再做骑士动作的位置,就返回false

你能明白为什么第一个(当然更复杂的)递归算法有效而你的无效吗?

于 2014-01-02T11:25:29.323 回答
0

除了“所有已访问”检查的问题外,我至少看到了一个问题,即在类字段中。当算法通过一个递归分支时,它会将一些方块标记为已访问,并且由于该信息是类字段,因此当它使当前分支失败并启动另一个分支时,它会看到以前尝试的所有无效信息。

如果您尝试传递positionsVisited,positionXpositionY作为方法参数并将其从类字段中删除,那么每个方法调用都会有它自己的实际副本怎么办?


最终版本

public class KnightChessRiddle {

    private final static Map<Integer, Character> letters = new HashMap<>();

    static {
        letters.put(0, 'a');
        letters.put(1, 'b');
        letters.put(2, 'c');
        letters.put(3, 'd');
        letters.put(4, 'e');
        letters.put(5, 'f');
    }

    public static void main(String[] args) {
        if (recursive(0, 0, 0, new boolean[6][6], 1, "")) {
            System.out.println("yes");
        } else {
            System.out.println("no");
        }
    }

    private static boolean allVisited(boolean[][] positionsVisited) {
        for (int i = 0; i < positionsVisited.length; i++) {
            for (int j = 0; j < positionsVisited.length; j++) {
                if (!positionsVisited[i][j]) {
                    return false;
                }
            }
        }
        return true;
    }

    private static boolean recursive(int positionX, int positionY, int moveType,
            boolean[][] positionsVisited, int moveCounter, String currentMoves) {

        // checks
        if (allVisited(positionsVisited)) {
            System.out.println(currentMoves);
            return true;
        }

        if (positionX > 5 || positionX < 0 || positionY > 5 || positionY < 0) {
            return false;
        }

        if (positionsVisited[positionX][positionY]) {
            return false;
        }

        // make move
        positionsVisited[positionX][positionY] = true;

        char c = letters.get(positionX);
        currentMoves += "" + c + (positionY + 1) + " (move type: " + (moveType + 1) + ")\r\n";

        if (recursive(positionX + 2, positionY + 1, 1, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 1, positionY + 2, 2, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 2, positionY - 1, 3, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX + 1, positionY - 2, 4, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 2, positionY + 1, 5, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 1, positionY + 2, 6, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 2, positionY - 1, 7, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else if (recursive(positionX - 1, positionY - 2, 8, cloneArray(positionsVisited), moveCounter + 1, currentMoves)) {
            return true;
        } else {
            return false;
        }

    }

    private static boolean[][] cloneArray(boolean[][] src) {
        boolean[][] newPositions = new boolean[src.length][src.length];
        for (int i = 0; i < src.length; i++) {
            System.arraycopy(src[i], 0, newPositions[i], 0, src[0].length);
        }
        return newPositions;
    } 
}

这是 6x6 板的工作变化。使用 8x8 板计算在我的机器上花费了太多时间。如果您随机选择移动选择,它可能会更快。

于 2014-01-02T11:54:56.657 回答