类似于 Knight-Tour-Problem 的回溯问题

问题描述

我正在尝试解决与骑士巡回赛问题类似的问题。问题:

一个骑士被放置在棋盘的左上角。给你一个数字向量(数字代表棋盘的平方,从左到右编号,从 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 步。