[英]Can someone help me with this knight's tour code?
代碼對我來說似乎很好,但是輸出太短而答案是肯定的,因為它應該是肯定的(從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
您實施的算法如下:
從正方形命令可能的騎士移動如下:
在每次移動中,選擇仍然允許的編號最小的移動。
(A)如果覆蓋所有方塊,則返回
true
。(B)如果你不能再采取行動,則返回
false
。
您的代碼有兩個問題。 第一個已經指出的是你錯過了支票(A)。 第二個更嚴重的問題是這個算法不起作用。 事實上,您最終會得到以下結果:
在這張照片中,黑色騎士代表開始和結束方塊,而白色騎士則是所有其他方塊。 如果你遵循你的算法,你最終會進入一個方形,你無法到達任何其他尚未覆蓋的方格。 這並不意味着你不能在棋盤上進行騎士之旅,只是你的算法不起作用。
如果這確實是你想要實現的算法,那么就沒有理由使用遞歸,因為for
循環也可以正常工作。 當有效騎士從我們當前所在的方格移動時,你的函數recursive
返回true。 有兩個原因,這實際上不是一個遞歸算法:
recursive
函數不是冪等的 - 它有副作用,即填充positionsVisited
數組的一個正方形。 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
。
你能看出為什么第一個(不可否認的是更復雜的)遞歸算法起作用而你的算法不起作用?
除了“全部被訪問”檢查的問題,我看到至少還有一個問題,即在類字段中。 當算法通過遞歸的一個分支時,它將一些正方形標記為已訪問,並且由於此信息是類字段,當它失敗當前分支並啟動另一個時,它會看到先前嘗試的所有無效信息。
如果您嘗試將positionsVisited
, positionX
和positionY
作為方法參數傳遞並將其從類字段中刪除,那么每個方法調用都將擁有它自己的實際副本,該怎么辦?
最終版本
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電路板計算會占用我的機器上太多時間。 如果你隨機選擇移動選擇可能會更快。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.