跳马问题
在5*5格的棋盘上,有一只中国象棋的马,从(1,1)点出发,按日字跳马,它可以朝8个方向跳,但不允许出界或跳到已跳过的格子上,要求其跳遍整个棋盘。输出跳遍整个棋盘的不同方案总数。
例如其中的一种跳法为:
1 16 21 10 25
20 11 24 15 22
17 2 19 6 9
12 7 4 23 14
3 18 13 8 5
输出
跳遍整个棋盘的不同方案总数。
思路分析
跳马的方向共有八个方向,如图:
可以用两个数组一一对应表示这个八个方向相对上一位置的偏移量:
int fx[8]={1,2,2,1,-1,-2,-2,-1};
int fy[8]={2,1,-1,-2,-2,-1,1,2};
然后使用回溯算法的经典模板解决该问题,模板如下:
void BackTrack(相应参数){
if(达到终止结束递归条件)
//说明此时得到一组相应的解,输出解或者收集结果并return结束递归
output();
return ;
}
for(循环条件为遍历的元素){
if(达到进入下一步的条件){
//处理结点,先保存上一步数据,便于后面回溯到上一步
SaveLastStep();
ChangeData(); //改变数据成为下一步的数据
BackTrack(相应参数); //递归进入下一层
//回溯上一步
DataChangeBack();
}
}
如果看官有兴趣深入了解回溯算法的原理,不妨自己调试一步一步运行理解。列举几种成功跳法的例子:

代码:
#include<iostream>
#include<cstdlib>
using namespace std;
class HorseJump{
public:
int x=0,y=0; //x,y用于记录棋子坐标,默认从(0,0)开始
//fx,fy表示棋子下一步行走的方向,根据题意共有8个方向
int fx[8]={1,2,2,1,-1,-2,-2,-1};
int fy[8]={2,1,-1,-2,-2,-1,1,2};
bool **checked; //记录棋子是否遍历过某点,遍历过标记为true,否则为false
int *track; //存储棋子的路径
static int num; //存储跳马的方法一共有多少种
public:
void init(){
//为checked分配内存,因为本题的track存储特性,所以25对坐标再增加一对(-1,-1)
track=new int[52];
checked=new bool *[5];
for(int i=0;i<5;i++)
checked[i]=new bool [5];
for(int i=0;i<5;i++)
for(int j=0;j<5;j++){
checked[i][j]=false;
}
checked[0][0]=true; //默认从(0,0)开始,标记为已访问
track[0]=track[1]=0;
for(int i=2;i<52;i++){
track[i]=-1;
}
}
//检查该坐标是否在棋盘内
bool check(int x,int y){
if(x>4||x<0||y>4||y<0)
return false;
else
return true;
}
void AddTrack(int x,int y,int *track){
for(int i=2;i<52;i++){
if(track[i]==-1){
track[i]=x; track[i+1]=y;
break;
}
}
}
void DeleteTrack(int *track){
for(int i=2;i<52;i++){
if(track[i]==-1){
track[i-1]=-1; track[i-2]=-1;
break;
}
}
}
//输出最终结果
void output(int *track){
for(int i=0;i<50;i++)
if(track[i]!=-1){
if(i%2==0)
cout<<track[i]<<",";
else if(i%2==1&&track[i+1]!=-1)
cout<<track[i]<<"->";
else if(i%2==1&&track[i+1]==-1){
cout<<track[i]; //last number
break;
}
}
cout<<"\n\n\n";
}
void BackTracking(int *&track,int x,int y,int step,bool **checked){
//如果棋子到达终点,那么达到递归结束条件
if(step==25){
//output(track);
num++;
return;
}
//cout<<"step "<<step<<": "; output(track);
for(int i=0;i<8;i++){
//下一步棋子在棋盘内且此点未被访问过
if(check(x+fx[i],y+fy[i]) == true&& checked[x+fx[i]][y+fy[i]]==false){
//如果下一步的坐标符合条件,则先保存上一步的结果,然后移动棋子向一下步走
int tempX=x,tempY=y;
x+=fx[i]; y+=fy[i];
checked[x][y]= true;
AddTrack(x,y,track); //将坐标(x,y)加入到track数组中
//cout<<"step "<<step<<": "; output(track);
BackTracking(track,x,y,step+1,checked); //递归进入下一步
//回溯到上一步
checked[x][y]=false;
x=tempX; y=tempY;
DeleteTrack(track);
}
}
}
};
int HorseJump::num=0;
int main(){
HorseJump h;
h.init();
h.BackTracking(h.track,h.x,h.y,1,h.checked);
cout<<HorseJump::num<<endl;
return 0;
}
结果是共有304种跳法
博客介绍了如何在5x5的棋盘上使用回溯算法解决中国象棋马按日字跳跃,遍历所有格子的问题。通过定义棋子的移动方向,设置状态数组和回溯函数,找出所有可能的跳法。代码实现详细展示了回溯过程,并输出了总共有304种不同的跳马方案。

628

被折叠的 条评论
为什么被折叠?



