以骑士身份在棋盘上找到所有 3 个角的路径

问题描述


#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,1,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 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,y - 1,y + 2,x - 2,x - 1,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 个角落的路径),或者我需要改变什么才能得到答案。

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

解决方法

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

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,y - 1,y + 2,x - 2,x - 1,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 条路径...

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

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

enter image description here

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

,

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

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

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

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

和例如*cor1 = 1;

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

int maincor1 =0;

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

从里面还是喜欢

goHorsie(board,cor3)

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