骑士之旅/递归

时间:2010-05-15 11:26:15

标签: java

我正在尝试学习更多关于递归的信息,但不知怎的,我无法解决骑士之旅,我希望有人可以指出我的逻辑错误。

class main {

    static int fsize = 5; // board size (5*5)
    static int board[][] = new int[fsize][fsize];
    static int[] move_x = {1, 2, 2, 1, -1, -2, -2, -1}; // possible moves (x-axis)
    static int[] move_y = {-2, -1, 1, 2, 2, 1, -1, -2}; // possible moves (y-axis)

    // x = current x coordinate
    // y = current y coordinate
    static void Solve(int move_number, int x, int y) {
        board[x][y] = move_number;

        // check whether the knight has been on all filds or not
        if (move_number == ((fsize * fsize) - 1)) {
            for (int i = 0; i < fsize; i++) {
                for (int c = 0; c < fsize; c++) {
                    System.out.printf("%3d", board[i][c]);
                }
                System.out.println("\n");
            }
        } 
        else {
            // calculate new board coordinates
            for (int i = 0; i < 8; i++) {
                for (int c = 0; c < 8; c++) {
                    // Check whether the new coordinates are valid or not
                    if ((x + move_x[i]) >= 0 && (x + move_x[i]) < fsize && (y + move_y[c]) >= 0 && (y + move_y[c]) < fsize) {
                        // check whether the knight has been on this field or not (-1 = hasn't been here)
                        if (board[x + move_x[i]][y + move_y[c]] == -1) {
                            System.out.println("Move: " + move_number + "\n");
                            // Find next field
                            Solve(move_number + 1, (x + move_x[i]), (y + move_y[c]));
                        }
                    }
                }
            }
            // couldn't find a valid move
            board[x][y] = -1;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < fsize; i++) {
            for (int c = 0; c < fsize; c++) {
                board[i][c] = -1;
            }
        }

        Solve(0, 0, 0);
    }
}

编辑:希望这没关系。我试图运行这个程序,但无法获得超过22个有效的动作。

4 个答案:

答案 0 :(得分:2)

我能够通过做两件事来修复你的代码:

  • 仅使用for (int i = 0; i < 8; i++)单级循环检查接下来的8种可能性
    • 为什么这里有两个嵌套循环?你只想检查那8种可能性,就是这样。
    • 每个级别只有8个动作,而不是64个
  • board[x][y] = -1;作为Solve中的最后一个语句
    • 无论if条件
    • 如何,都希望执行此操作
    • 要在退出此级别之前撤消board[x][y] = move_number;

修好后,你的作业完成,正确,完成。恭喜!


在伪代码中

现在,这就是你所拥有的:

static void Solve(int move_number, int x, int y) {
    board[x][y] = move_number;

    if (DONE) {
       PRINT;
    } else {
        for (int i = 0; i < 8; i++) {
            for (int c = 0; c < 8; c++) {
                ATTEMPT_MOVE(i, c); // doesn't make sense!!!
            }
        }
        board[x][y] = -1; // this doesn't belong here!!!
    }
}

要修复此代码,您只需执行此操作:

static void Solve(int move_number, int x, int y) {
    board[x][y] = move_number;

    if (DONE) {
       PRINT;
    } else {
        for (int i = 0; i < 8; i++) {
           ATTEMPT_MOVE(i); // now makes more sense!
        }
    }

    board[x][y] = -1; // must undo assignment in first line!
}

额外建议

  • 将逻辑分解为辅助方法,即电路板打印,并检查有效坐标

答案 1 :(得分:1)

嗯,所以我试了一下,试图找出发生了什么。这是我可以收集的内容。

  1. sprung_xsprung_y应该一起移动,因为它们代表骑士的移动。你有ci作为移动这些数组的独立索引,但实际上它应该只是1个变量。我触及内部for循环,并在i的任何地方使用了c
  2. 在方法SucheWeg结束时,您将重新调用的单元格重置为-1。这给我带来了无限循环。删除该线允许它在单元1,2的19次移动中正常完成。根据Knight巡回赛的定义,即1次攻击远离你开始的单元格(0,0),因此代表完整的巡回演出。 / LI>
  3. 根据Wikipedia,您的fsize 5可能无法完成。我用6代替测试。
  4. 您需要考虑到达最后时会发生什么。在您的代码中,最后一步是打印输出,但方法SucheWeg仍将继续运行,并且需要一种正常终止的方法。你必须允许展开决定,如果你达到了死胡同(我认为-1重置来自#2),但也意识到如果你不小心的话,同样的展开将使它永远存在。 **从方法返回时,应在撤消步骤之前检查电路板是否已满。如果它已满,你就到了最后。

  5. 只是为了向您展示我使用的代码:

    static boolean isFull(int b [][])
    {
       for(int i = 0; i < b.length; i++)
       {
          for(int k = 0; k < b[i].length; k++)
          {
             if(b[i][k] == -1) return false;
          }
       }
       return true;
    }
    
    static void SucheWeg(int schrittnummer, int x, int y)
    {
       board[x][y] = schrittnummer;
       if(schrittnummer == ((fsize * fsize) - 1)) return;
    
       for(int i = 0; i < 8; i++)
       {
          int nextX = x + sprung_x[i];
          int nextY = y + sprung_y[i];
    
          // if we can visit the square, visit it
          if(nextX >= 0 && nextX < fsize && nextY >= 0 && nextY < fsize)
          {
             if(board[nextX][nextY] == -1)
             {
                SucheWeg(schrittnummer + 1, nextX, nextY);
             }
          }
       }
       if(isFull(board)) return;  // this is how we avoid resetting to -1
       board[x][y] = -1;          // reset if you get this far, so you can try other routes
    }
    

    然后在main我为我们打印出来:

    for(int i = 0; i < fsize; i++)
    {
       for(int c = 0; c < fsize; c++) System.out.format("%02d ", board[i][c]);
       System.out.println("");
    }
    

    输出是:

    00 33 28 17 30 35 
    27 18 31 34 21 16 
    32 01 20 29 10 05 
    19 26 11 06 15 22 
    02 07 24 13 04 09 
    25 12 03 08 23 14
    

    我想说最后一件事 - 这个算法的一个很好的实现会捕获无限循环。如果这实际上是家庭作业,你应该修改它,直到你可以在任何尺寸的板上运行它而不用担心无限循环。目前,如果没有解决方案,它可能会永远运行。

答案 2 :(得分:1)

这就是事情 - 即使第一次尝试移动成功(导致解决方案),您也在尝试不同的有效移动。我会让函数返回一个布尔值,指定它是否达到了解决方案。因此,当您从自身调用函数时,如果它返回false,则应该只尝试下一个有效移动。此外,当您尝试不同的移动时,您应该清除之前的移动(因为阵列已更改)。


修改

class main {

static int fsize = 5; // board size (5*5)
static int board[][] = new int[fsize][fsize];
static int[] move_x = {1, 2, 2, 1, -1, -2, -2, -1}; // possible moves (x-axis)
static int[] move_y = {-2, -1, 1, 2, 2, 1, -1, -2}; // possible moves (y-axis)

// x = current x coordinate
// y = current y coordinate
static boolean solve(int move_number, int x, int y)
{
    boolean ret = true;
    board[x][y] = move_number;
    if(move_number == ((fsize * fsize) - 1))
    {
        for(int i = 0; i < fsize; i++)
        {
            for(int c = 0; c < fsize; c++)
            {
                System.out.printf("%3d", board[i][c]);
            }
            System.out.println("\n");
        }
    }
    else
    {
        for(int i = 0; i < 8; i++)
        {
            if((x + move_x[i]) >= 0 && (x + move_x[i]) < fsize
                    && (y + move_y[i]) >= 0
                    && (y + move_y[i]) < fsize)
            {
                if(board[x + move_x[i]][y + move_y[i]] == -1)
                {
                    if (solve(move_number + 1, (x + move_x[i]), (y + move_y[i]))) {
                        break;
                    }
                }
            }
        }
        ret = false;
        board[x][y] = -1;
    }
    return ret;
}
public static void main(String[] args) {
    for (int i = 0; i < fsize; i++) {
        for (int c = 0; c < fsize; c++) {
            board[i][c] = -1;
        }
    }

    solve(0, 0, 0);
}

}

返回:

 0 15 20  9 24

 19 10 23 14 21

 16  1 18  5  8

 11  6  3 22 13

答案 3 :(得分:0)

因为它看起来有点像家庭作业问题,所以我只是从一个提示开始。

move_xmove_y是骑士可能的x,y移动。但是,这些可以独立编制索引(ic)。你可能希望重新考虑一下。