问题描述
#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
开头。那时 x
和 y
可能具有板外的值。所以你做越界访问。
在访问数组之前检查 x
和 y
的值。
喜欢:
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 + 1
和 y - 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 个角。
,此答案基于用户 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 指针变量。