马的遍历问题:在n*m的棋盘上,马只能走日字。马从位置(x,y)处出发,把棋盘的每一点都走一次,且只走一次,找出所有路径。
问题分析:行n,列m,马在不出边界的情况下有8个方向可以行走(走日字),如当前坐标为(x,y),则行走后的坐标可以为:
(x+1,y+2) (x+1,y-2), (x+2,y+1) (x+2,y-1)
(x-1,y-2) (x-1,y+2) (x-2,y-1) (x-2,y+1)
回溯法算法设计: 搜索空间是nM个点,约束条件是不出边界且每个点只经过一次,节点的扩展规则如上所述。
搜索过程是从任一点(x,y)出发,按深度优先原则,从8个方向尝试一个可以走的点,直到走过nm个点。用递归容易实现。
注意:问题要求找出所有可能的解。就要注意回溯过程的清理现场工作,就是置当前位置为未经过。
数据结构设计:用一个变量dep记录递归深度,也就是走过的点数。当dep=nm;找到一组解。
用nm的二维数组记录马走过的过程,初始值为0表示未经过,起点存储的是1,终点存储的是n*m。
#include<stdio.h>
const int n=5,m=4;
int fx[8]={1,2,2,1,-1,-2,-2,-1};//定义马走的8个方向
int fy[8]={2,1,-1,-2,-2,-1,1,2};
int A[n+1][m+1];//如果为0表示当前节点未经过,非0值a表示该节点为走的第a步
int dep,x,y,numbers=0;//分别表示第几步、横坐标、纵坐标、满足条件的棋盘数
bool check(int x,int y)
{
if(x>=1 && x<=n && y>=1 &&y<=m && (!A[x][y]))
return true;
else
return false;
}
void output()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
printf("%d ",A[i][j]);
}
printf("\n");
}
printf("----------\n");
}
void solve(int x,int y,int dep)
{
int i,xx,yy;
for(i=0;i<=7;i++)
{
xx=x+fx[i];
yy=y+fy[i];
if(check(xx,yy))
{
A[xx][yy]=dep;
if(dep==n*m)//当步数达到n*m时表示已走完每一个节点
{
numbers++;
output();
}
else
{
solve(xx,yy,dep+1);
}
A[xx][yy]=0;//将走过的点重置为未走过
}
}
}
int main()
{
printf("please input x and y:\n");
scanf("%d %d",&x,&y);
if(x>n||y>m||x<1||y<1)
{
printf("input error\n");
}
else
{
printf("----------\n");
A[x][y]=1;
solve(x,y,2);
printf("%d\n",numbers);
}
}