【问题标题】:Find path to all 3 corners on a chessboard as a knight以骑士的身份找到通往棋盘上所有 3 个角的路径
【发布时间】:2021-07-31 12:35:49
【问题描述】:

#include <stdio.h>

#define SIDE 8

#define VISITED 1
#define NOT_VISITED 0

#define FALSE 0
#define TRUE !FALSE

void printBoard(int board[][SIDE]);
int goHorsie(int board[][SIDE], int x, int y, int step, int cor1, int cor2, int cor3);

int main(void)
{

    int board[SIDE][SIDE] = { NOT_VISITED };
    int found = 0;

    found = goHorsie(board, 0, 0, 1, 0, 0, 0);

    if (found)
    {
        printf("Yes, there is a path from 0,0 through all corners! Here it is:\n");
        printBoard(board);
    }
    else
    {
        printf("No path from 0,0 through all corners\n");
        printBoard(board);
    }

    getchar();
    return 0;
}

int goHorsie(int board[][SIDE], int x, int y, int step, int cor1, int cor2, int cor3)
{
    int res = FALSE, check = 1;
    if (board[x][y] != NOT_VISITED //We were here already!
        || x >= SIDE || y >= SIDE || x < 0 || y < 0)
    {
        res = FALSE;
        check = 0;
    }

    if (x == 7 && y == 7)
    {
        printf("1)found!\n");
        cor1 = 1;
    }
    if (x == 7 && y == 0)
    {
        printf("2)found!\n");
        cor2 = 1;
    }
    if (x == 0 && y == 7)
    {
        printf("3)found!\n");
        cor3 = 1;
    }
    if (cor1 == 1 && cor2 == 1 && cor3 == 1)
    {
        printf("FOUND ALL!\n");
        return TRUE;
    }

    else if(check == 1)
    {
        board[x][y] = step;
        step++;
        res =
            goHorsie(board, x + 1, y - 2, step, cor1, cor2, cor3) ||
            goHorsie(board, x + 2, y + 1, step, cor1, cor2, cor3) ||
            goHorsie(board, x + 2, y - 1, step, cor1, cor2, cor3) ||
            goHorsie(board, x + 1, y + 2, step, cor1, cor2, cor3) ||
            goHorsie(board, x - 2, y + 1, step, cor1, cor2, cor3) ||
            goHorsie(board, x - 2, y - 1, step, cor1, cor2, cor3) ||
            goHorsie(board, x - 1, y + 2, step, cor1, cor2, cor3) ||
            goHorsie(board, x + 1, y - 2, step, cor1, cor2, cor3);
            
        if (!res)
        {
            board[x][y] = NOT_VISITED;
        }
    }
    return res;
}


void printBoard(int board[][SIDE])
{
    int i = 0, j = 0;
    for (int i = 0; i < SIDE; i++)
    {
        for (int j = 0; j < SIDE; j++)
        {
            printf("%3d", board[i][j]);
        }
        printf("\n");
    }
}

我正在使用递归来找到所有 3 个角的路径。

我现在运行程序大约 20 分钟,但仍然没有找到解决方案。

我知道为什么它花了太长时间,但不确定它是否能让我得到答案,我认为它会永远循环。

所以我的问题是我是否使函数正确,它最终会给我正确的答案(通往所有 3 个角落的路径),或者我需要更改什么才能获得答案。

我所说的三个角是:右上角、右下角和左下角。

【问题讨论】:

  • 你需要展示你如何初始化board以及你如何进行第一次函数调用
  • 我不清楚您要查找的内容。是 a) 一个 路径让您访问所有三个角落还是 b) 三个路径,即每个角落一个路径?
  • 投票只是因为你的代码的函数名字太棒了:goHorsie
  • 我试图找到 1 条覆盖所有 3 个角的路径
  • 如果您引入宏(常量会更好),请使用它们来避免幻数。例如。 7 -> SIDE-1。如果您开始有一个变量res 的概念,请保持一致并且没有两个返回。你的if-tree 应该有更多的else ifs。您存储在cor123 中的信息仅用于更深层次的递归。如果您留下一次递归,则有关已找到角点的信息将丢失。使用指向int 的指针。

标签: c recursion chess knights-tour


【解决方案1】:

可能还有其他错误,但这里有一个:

if (board[x][y] != NOT_VISITED || x >= SIDE || y >= SIDE || x < 0 || y < 0)

此表达式的计算以board[x][y] != NOT_VISITED 开始。那时xy 的值可能在棋盘之外。因此,您可以进行越界访问。

在访问数组之前检查xy的值。

喜欢:

if (x >= SIDE || y >= SIDE || x < 0 || y < 0 || board[x][y] != NOT_VISITED)

另一个问题是你检查“找到”之前你检查你是否已经访问过那个角落一次。这将给出一些“找到”的意外打印。

这一行:

if (x >= SIDE || y >= SIDE || x < 0 || y < 0 || board[x][y] != NOT_VISITED)

应该是函数中的第一个语句。

另一个错误

        goHorsie(board, x + 1, y - 2, step, cor1, cor2, cor3) ||
        goHorsie(board, x + 2, y + 1, step, cor1, cor2, cor3) ||
        goHorsie(board, x + 2, y - 1, step, cor1, cor2, cor3) ||
        goHorsie(board, x + 1, y + 2, step, cor1, cor2, cor3) ||
        goHorsie(board, x - 2, y + 1, step, cor1, cor2, cor3) ||
        goHorsie(board, x - 2, y - 1, step, cor1, cor2, cor3) ||
        goHorsie(board, x - 1, y + 2, step, cor1, cor2, cor3) ||
        goHorsie(board, x + 1, y - 2, step, cor1, cor2, cor3);

第一行和最后一行使用相同的表达式,即x + 1y - 2。所以你的代码没有涵盖所有 8 个动作。一招两次。

我现在运行程序大约 20 分钟,但仍然没有解决问题。

一旦您修复了上面报告的错误,您可以尝试一下...但是如果您在 20 分钟内仍然没有解决方案,请不要感到惊讶。

问题是有太多的方法可以检查,即使是现代计算机也会花费相当长的时间来解决这个问题。

考虑一下:

step 1: 8 paths
step 2: 8 paths
step 3: 8 paths
...
step 16: 8 paths

总共 281.474.976.710.656 条路径....好吧,这还不错,因为其中许多路径会在到达第 16 步之前停止,因为马离开了棋盘或返回到相同的位置。但仍然......有很多途径可以检查。

可以分 16 步完成吗?它需要 20 个步骤.. 即 1.152.921.504.606.846.976 或者它甚至可能需要 64 个步骤!? 8^64 路径检查...

因此,要找到解决方案,您应该以不同的方式思考,而不仅仅是暴力检查。

通过固定拐角被访问的顺序并通过对移动方向设置一些限制,我想出了这个:

此解决方案表明您可以在 20 步中访问所有 4 个角。

【讨论】:

    【解决方案2】:

    此答案基于用户 4386427 的答案,将它们放在一起以获得完整的解决方案。

    对于int goHorsie(int board[][SIDE], int x, int y, int step, int cor1, int cor2, int cor3);,您始终使用当前递归中是否找到三个角的信息副本。如果您确实找到了一个或两个角,但不是在一次递归调用中找到所有三个角,那么一旦离开递归,这些新发现的真实值就会丢失。在下一次调用中,您可能会找到丢失的角落,但不会注意到,因为之前找到的信息已丢失。

    为了保留有关查找的信息,您可以切换到指针参数。

    int goHorsie(int board[][SIDE], int x, int y, int step, int* cor1, int* cor2, int* cor3);

    例如*cor1 = 1;

    为此你需要在main()中引入相应的可引用变量

    int maincor1 =0;

    并从 main() 调用,例如 found = goHorsie(board, 0, 0, 1, &amp;maincor1, &amp;maincor2, &amp;maincor3);

    从里面还是喜欢

    goHorsie(board, x + 1, y - 2, step, cor1, cor2, cor3)

    这是相同的代码,但现在使用与以前同名的指针变量。

    【讨论】:

    • 它现在工作得更好,但仅在位置 (7,7) 它保持为 0,就像它没有通过它一样输出示例:pastebin.com/yzVasyBc
    • 您可能需要就更改的代码提出一个新问题。确保在此处合并两个(所有)答案的提示。也许您可以在此处更新问题,但要非常小心,不要使任何现有答案无效,即有必要保留答案所讨论的错误代码版本,并且只添加更多信息,例如“编辑:合并前几个答案的提示,我有这个代码......现在的不当行为是......”。但这是有风险的。最好提出一个新问题。您可以在此处接受答案(艰难的选择...),基于哪个解决了更多问题。评论其他人。
    • 如果您发现两个角但不是最极端的一个,我怀疑您错过了另一个答案的“两倍相同”提示。请三重检查正在检查的 8 个独特动作。我提到这一点是因为我对另一个答案的那部分印象特别深刻。 (该死,我只是给出了接受另一个答案的理由。叹息。)
    • 我刚刚修复了同样的问题:pastebin.com/0usfMdqx
    • 看来你没有明白我的cmets的意思,问一个新问题。我能帮你理解我的意思吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-03
    • 1970-01-01
    相关资源
    最近更新 更多