马的遍历问题

问题很常见喽,也成为骑士巡逻问题。中国象棋中“马走日“,现在有一匹马和一个8X8的棋盘,在棋盘中任意位置。试着求解出一种方案,让这匹马遍历棋盘中的每一个节点,每个节点只能访问一次。
很明显是一个回溯的题目,不过最关键的部分就是优化了。我一开始用了穷举的方式。不过计算量实在太大了,计算机跑了一上午都没有算出来。后来查资料才知道有一种优化的方法,也称为Warnsdorff规则,具体就是:Warnsdorff规则指在所有可走且未经过的方格中,马只可能走这样一个方格:从该方格出发,马能跳的方格数最少;如果可跳的方格数相等,则从当前位置看,方格序号小的优先。依照这一规则往往可以找到一条路径但是并不一定能够成功。
也就是说,举例来说,如果马A从当前位置出发可以到达且合法的节点有3个,分别是B,C,D。同时从B,C,D出发可以到达且合法的节点分别为1,4,2个。则我们就选择B点,这就是上面Warnsdorff原则的含义。下面我给出一个简单的代码实现:

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;
#define N 8
const int step[8][2]={{1,2},{2,1},{1,-2},{2,-1},{-1,2},{-2,1},{-1,-2},{-2,-1}};
class Pos{
public:
  int x,y;
  Pos(int _x,int _y):x(_x),y(_y){};
};
bool visited[N][N];
bool check(int x,int y){
  if(x>=0 && x<N && y>=0 && y<N && visited[x][y]==false)
    return true;
  return false;
}
vector<Pos> ivec;
//计算当前点的可落子位置
int exitn(int x,int y,int a[]){
  int count,xx,yy,k;
  for(k=0,count=k;k<N;k++){
    xx=x+step[k][0];
    yy=y+step[k][1];
    if(check(xx,yy))
      a[count++]=k;
  }
  return count;
}             
int select_min_pos(int x,int y){
  int arr[N],count=exitn(x,y,arr),pos,b[N];
  if(count==0)
    return -1;
  int min,k;
  for(min=8,k=0;k<count;k++){
    int temp=exitn(x+step[arr[k]][0],y+step[arr[k]][1],b);
    if(temp<min){
      min=temp;
      pos=arr[k];
    }
  }
  return pos;
}
void print(){
  int i=1;
  for(auto it=ivec.begin();it!=ivec.end();it++)
    cout<<"第"<<i++<<"步"<<"("<<(*it).x<<","<<(*it).y<<")"<<endl;
}
int main(void){
  int start_x,start_y;
  cout<<"请输入马的起始位置: ";
   while(1){
    cin>>start_x>>start_y;
      if(!check(start_x,start_y))
      cout<<"输入错误,清重新输入."<<endl;
      else
    break; }
  memset(visited,sizeof(bool)*N*N,0);
  visited[start_x][start_y]=true;
  int move=1,flag;
  Pos v(start_x,start_y);
  ivec.push_back(v);

  for(move=2;move<=N*N;move++){
      if((flag=select_min_pos(start_x,start_y))==-1)
          break;
      start_x=start_x+step[flag][0];
      start_y=start_y+step[flag][1];
      visited[start_x][start_y]=true;
      Pos v(start_x,start_y);
      ivec.push_back(v);
  }
  if(move>=64)
    cout<<"Yeah we found a way to scan this chess"<<endl;
  print();
  return 0;
}

以上就是简单的实现。
参考资料:
http://zh.wikipedia.org/wiki/%E9%A8%8E%E5%A3%AB%E5%B7%A1%E9%82%8F
http://blog.csdn.net/xuguangsoft/article/details/8093140
感谢:SimonS

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值