骑士巡游问题
1、问题描述
在N行N列的棋盘上,一位骑士按象棋中“马走日”的走法从初始坐标位置(Sx,Sy)出发,要求遍历(巡游)棋盘中每一个位置一次,请输出骑士巡游的位置顺序,或输出无解。
2、问题分析
对于这个问题,我们可以用回溯法来处理。设骑士的当前坐标为q(x,y),则它的下一步的坐标可能为:
q(x+2,y+1) —— 右下走“日” (念ri,即竖着走)
q(x+1,y+2) —— 右下走“曰”(念yue,即横着走)
q(x-1,y+2) —— 右上走“曰”
q(x-2,y+1) —— 右上走“日”
q(x-2,y-1) —— 左上走“日”
q(x-1,y-2) —— 左上走“曰”
q(x+1,y-2) —— 左下走“曰”
q(x+2,y-1) —— 左下走“日”
由于骑士总是从坐标q(1,1)出发,因此可将骑士巡游的第一步固定为坐标q(1,1),其下一步由如上所述顺序依次试探,并且设以标记数组,用于标记每次试探的位置,若试探成功,则标记位置置为当前可行步数(即递归函数内的递归层数),若试探失败,则回溯至上一步。反复执行上述步骤,直至可行步数为n×n为止。
3、算法策略与实现源码
设一标记数组int m[n+1] [n+1](为方便计算,数组皆多开一个或一系空间),用于标记每次试探的位置,设一个二维数组next[9] [2]
用于计算下一步的试探坐标,设以整型变量step,用于记录可行步数,设以递归函数tour(int step,int x,int y),其函数返回类型为bool,当step=n×n+1时,表示巡游成功。
#include<iostream>
#include<iomanip>
using namespace std;
const int n = 5;
int m[n+1][n+1];
int next[9][2] = {{0,0},{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};
bool tour(int step,int x,int y){
if(step == n*n + 1) return true;
bool q = false;
for(int k = 1;k <= 8; k++){
int u = x + next[k][0];
int v = y + next[k][1];
if(u >= 1 && u <= n && v >= 1 && v <= n){
if(!m[u][v]){
m[u][v] = step;
q = tour(step + 1,u,v);
if(!q) m[u][v] = 0;
}
}
}
return q;
}
int main(){
m[1][1] = 1;
if(tour(2,1,1)){
for(int i = 1;i <= n; i++){
for(int j = 1; j <= n; j++)
cout << setw(4) << m[i][j];
cout << endl;
}
}
else cout << "no solution" << endl;
}