问题描述
我正在尝试解决与骑士巡回赛问题类似的问题。问题:
一个骑士被放置在棋盘的左上角。给你一个数字向量(数字代表棋盘的平方,从左到右编号,从 1 到 64)。骑士必须一个接一个地到达向量中的那些方格,最后您应该输出骑士所走的路径。请注意,骑士可能会多次访问一个方格。
我尝试使用回溯来解决类似于 Knight-Tour Problem 的这个问题。然而,我被困在“不止一次访问一个广场”。如果我不设置任何条件,骑士只会在 2 个方格之间来回跳跃,永远不会到达任何地方。我还尝试以某种方式限制骑士返回它之前出现的方格,但后来我得到了一个更大的循环。 在这种特殊情况下,我是否遗漏了什么或回溯一般是错误的?
下面是我的代码。请注意,我只使用右下角的方块作为骑士到达它的目标,作为一个简单的测试,即便如此,程序还是失败了,调试器显示骑士仍在无限循环中行走。
#include <iostream>
#include <cmath>
using namespace std;
const int n = 8;
bool found = 0;
int *path = new int[n*n];
int pathindex=0;
bool visited[n][n];
void print(int (*matrica)[n]); //function to print a matrix
void printpath(int path[n*n]){
for(int i=0;i<pathindex;i++)
cout<<path[i]<<" ";
cout<<endl;
}
void knight(int (*matrica)[n],int x,int y,int path[n*n],int &pathindex){
if(found){
return;
}
if (x==7 && y == 7){ //if knight landed on the target,output the result
found = 1;
path[pathindex]=matrica[7][7];
pathindex++;
printpath(path);
return;
}
if(x > n-1 || y > n-1 || x < 0 || y < 0){ //if coordinates out of bounds,dismiss
return;
}
path[pathindex]= matrica[x][y];
pathindex++;
//all possible knight moves
if(path[pathindex-1]!=matrica[x-1][y+2]) //conditions to test if the next square was the one we came from
knight(matrica,x-1,y+2,path,pathindex);
if(path[pathindex-1]!=matrica[x-2][y+1])
knight(matrica,x-2,y+1,pathindex);
if(path[pathindex-1]!=matrica[x+1][y+2])
knight(matrica,x+1,pathindex);
if(path[pathindex-1]!=matrica[x+2][y+1])
knight(matrica,x+2,pathindex);
if(path[pathindex-1]!=matrica[x+2][y-1])
knight(matrica,y-1,pathindex);
if(path[pathindex-1]!=matrica[x+1][y-2])
knight(matrica,y-2,pathindex);
if(path[pathindex-1]!=matrica[x-1][y-2])
knight(matrica,pathindex);
if(path[pathindex-1]!=matrica[x-2][y-1])
knight(matrica,pathindex);
pathindex--;
}
int main(){
int matrica[n][n];
int k = 1;
for(int i=0;i<n;i++){ //number the chess board
for(int j=0;j<n;j++){
matrica[i][j] = k;
k++;}
}
print(matrica);
knight(matrica,pathindex);
if(!found)
cout<<"No Solution!";
return 0;
}
void print(int (*matrica)[n]){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(matrica[i][j]<10)
cout<<" "<<matrica[i][j]<<" ";
else
cout<<matrica[i][j]<<" ";
}
cout<<endl;
}
cout<<endl<<endl;
}
如何实现骑士可以多次使用同一个方块,但它不能在原来的循环上行走的部分?
请注意,这与家庭作业或学校无关,只是为了我个人的乐趣。
解决方法
如果按顺序访问正方形,这很容易;弄清楚如何从 a 到 b。基本寻路。然后连接。
如果正方形的数量很少,只需对每个排列执行上述操作。所有对的 a 到 b 的排序成本(b 到 a 是相同的)。然后解决旅行商找到最短路线。
假设第一个,或者如果您不关心最短,这是“从 a 到 b 的路径查找”。这样做时你可以假设它永远不会循环,因为那样不循环也会起作用。
简单的解决方案是直接绘画。有一个充满 -1 的 8x8 网格。写 0。然后在所有从 0 为 -1 的骑士移动上写 1。然后在所有从 1 开始为 -1 的骑士移动上写下 2。重复。
如果你想变得更花哨,你可以用位操作来绘画。有 8 个 8 位值。在骑士开始的地方画一个 1 位。一系列的转变和面具可以盲目地转换“骑士可以到达”的面具。存储每组移动的可达掩码,直到到达目标方格。
现在回溯 - 从目标方格移动到先前快照中的任何合法移动。然后重复,直到你得到原来的骑士方块。
newrow[i]=row[i-2]<<1 | row[i-2]>>1 | row[i+2]<<1 | row[i+2]>>1 | row[i-1]<<2 | row[i-1]>>2 | row[i+1]<<2 | row[i+1]>>2;
进行边界检查,使 row[out of bounds]
为 0,上限为 8 位,每 8 行循环一次,然后您向前或向后采取可到达的骑士掩码 1 步。