Knight’s tour problem is a special case of Travelling Salesman Problem(TSP) or Hamiltonian Cycle Problem(HCP).[1]
The problem is that, on a chessboard, the knight needs to jump over the entire chessboard, only one jump per grid, and finally return to the starting point.
2. Solution
Obviously, we can use DFS and backtracking to solve the problem. For every point, there are 8 directions to search. If all 8 directions are impossible, we do the backtracking.
The result is correct. But obviously, the time cost is too large because every point has 8 choices, by Multiplication Principle, the time complexity is $O(8^{n^2})$, where $n$ is the chessboard size.
3. Optimize by using greedy algorithm
We can choice the direction greedily to increase the success probability. That is to say, we should first choose the most difficult point to arrive. The fewer available directions one point has, it’s more difficult to access. If we first choice the most difficult point, the remaining points are easier to access, the success probability will increase, then the backtracking times and time cost will certainly decrease.[2]
We use below method to compute the number of available directions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
staticint[][] move={{-2,1},{-1,2},{1,2},{2,1},{2,-1},{1,-2},{-1,-2},{-2,-1}};//stores the direction publicstaticintpossibleDirections(int x,int y){ if(x<0 || x>=boardSize || y<0 || y>=boardSize){ return9;//out of the board } int count=0; for (inti=0; i < 8; i++) {//scan all 8 directions int nextX=x+move[i][0]; int nextY=x+move[i][1]; if(nextX>=0 && nextX<boardSize && nextY>=0 && nextY<boardSize && chessboard[nextX][nextY]==0){ count++; } } return count; }
Based on the method, we rewrite the DFS code as below.
Direction[] directions = newDirection[8];//stores (index of move[]) and (number of available directions) for (inti=0; i < 8; i++) { int nextX=x+move[i][0]; int nextY=y+move[i][1]; intdirectionNum= possibleDirections(nextX, nextY); directions[i]=newDirection(i,directionNum); } Arrays.sort(directions);//we should first choose the most difficult point to arrive, so we sort the directions for (inti=0; i < 8; i++) { int index=directions[i].index;//the index of move[] dfs(x+move[index][0],y+move[index][1],step+1); backCount++; } publicstaticclassDirectionimplementsComparable<Direction>{ int index;//index of move[] int directionNum;//the number of available directions
The backtracking time decreases from $10^9$ to $10^6$. The time cost decreases from 11.26s to 0.60s.
Because of the greedy strategy, the knight tends to travel along the edge of the chessboard. Because the points on the edge have fewer directions to access.[2]
4. Other method
We can also use divide and conquer to solve the problem. We can divide the chessboard into several small blocks. As long as each small block satisfies the feature of “starting from any point and passing through all other points without repetition”, connecting the paths of each small block can form a ring that passes through all the grids.[2]
For example, we can divide a 9x10 chessboard to two 5x6 block and one 3x10 block as below