[易读易懂] 骑士游历算法 Knight's Tour Problem

原创 2018年04月17日 10:53:11

1、问题描述

在一个N*M的棋盘上,在任意位置放置一个骑士,骑士的走"日字",和象棋中的马一样。

问该骑士能否不重复遍历整个棋盘。下面的方法本质还是穷举,所以就写成可以计算出共有多少种不同的遍历方法。

2、分析与思路

根据题意,骑士走的下一步可能在棋盘上有多种选择(最多8种),需要选择1种,然后继续走下去,直到无处可走。

无处可走时有两种情况:

情况一:成功完成了遍历,那么接下来就通过回溯(回到上一步的位置,重新选择下一步的位置),寻找其他的走法。

情况二:未完成遍历,接下来还是要通过回溯继续寻找能够完成遍历的走法。

以上可以知这是一个DFS(深度优先搜索)问题,并且需要回溯。

3、代码(Java版)

算法可以统计出共有多少中不同的遍历方法,以及多少种失败的尝试。并可以给出每次无法前进时棋盘的状态和每步走法。

/*
 * Quesion: Kight's tour in n*m board  骑士(棋盘上走日字)游历问题,n*m棋盘,从角出发,能否不重复的遍历整个棋盘,有几种不同的遍历方法
 * Author: Mingshan Jia
 * Date: 2018/4/16
 * */
/* 
 *┼——┼——┼——┼——┼——┼  
 *│  │ 4│  │5 │  │  
 *┼——┼——┼——┼——┼——┼  
 *│ 3│  │  │  │6 │       
 *┼——┼——┼——┼——┼——┼        
 *│  │  │█ │  │  │        每走一步后按照1~8的位置次序去尝试走下一步,DFS
 *┼——┼——┼——┼——┼——┼  
 *│ 2│  │  │  │7 │   
 *┼——┼——┼——┼——┼——┼ 
 *│  │ 1│  │8 │  │   
 *┼——┼——┼——┼——┼——┼    
 **/	
package com.exercise;

import java.util.ArrayList;
import java.util.Collections;

public class Solution { 
	
	static final int[] xMove= {2,1,-1,-2,-2,-1,1,2};        //xMove和yMove一起组成每一步的偏移量
	static final int[] yMove= {-1,-2,-2,-1,1,2,2,1};

	public void solveKnightTravel(int n,int m) {
		
	    int weight=0; //权重值代表第几步走到该位置;
	    int count=0; //完全遍历的解数;
	    int countFail=0; //失败尝试次数
		int[][] A=new int[n][m];	
	    ArrayList<Boolean> resList=new ArrayList<Boolean>();  //存储无法继续走时棋盘的状态(false或true,true代表遍历完成)
	    
		knightJump(A,n-1,0,resList,weight);   //核心DFS,从(n-1,0)的位置开始跳	
		
		count=Collections.frequency(resList, true);   //true的次数:不同的遍历数
		countFail=Collections.frequency(resList, false);//false的次数:失败的走法
		System.out.println("一共有"+count+"种不同的遍历方法");  //3*4的情况下有2种走法,5*5有304种
		System.out.println("一共有"+countFail+"次失败的尝试"); 
	}
	
	public void knightJump(int[][] A, int row, int col,ArrayList<Boolean> resList,int weight){	
	  boolean sign=false;  //遍历完成标志
	  
	  if(!displacable(A,row,col)) {  //若该位置可走
		  weight++;
	      A[row][col]=weight;
	   
	      if(nowhereCanGo(A,row,col)) { //走到无路可走时,绘出棋盘的状态图,并检测是否完成
			  printBoard(A);
			  if(traverseCompleted(A)) {			
				  sign= true;
			  }
			  resList.add(sign);  
			  System.out.println(sign); 
		  }
	 
		  for(int k=0;k<8;k++) {    //走下一步
			  int nextRow=row+xMove[k];
			  int nextCol=col+yMove[k];
			  
			  if (nextRow<0 || nextRow>=A.length || nextCol<0 || nextCol>=A[0].length) {  
                  continue;  
              }
			  
			  knightJump(A,nextRow,nextCol,resList,weight);  		
		  }	
		  
		  A[row][col]=0; //回溯的操作,来到这里说明该位置下一步不可走,于是将该位置置0并减少权重,也就是说上一步不选择走此处,而去尝试下一个位置
		  weight--;
	   }
	}
	
	public boolean nowhereCanGo(int[][] A, int row, int col) {   //检测该位置是否无处可走
		
		boolean res=true;
		for(int i=0;i<8;i++) {
			res=res&&displacable(A,row+xMove[i],col+yMove[i]);
		}
		return res;	
	}
	
	public boolean displacable(int[][] A,int row,int col) {       //检测该位置能否放置
		if(!(row>=0&&row<A.length&&col>=0&&col<A[0].length&&A[row][col]==0))
			return true;
		else 
			return false;	
	}
	
	public void printBoard(int[][] A) {         //无法继续行走时画出棋盘
		for (int i = 0; i < A.length; i++) {  
            for (int j = 0; j < A[0].length; j++) {  
                if (A[i][j] < 10) {  
                    System.out.print(" " + A[i][j]);  
                } else  
                    System.out.print(A[i][j]);   
                System.out.print(" ");  
            }  
            System.out.println(); 
		}
	}
	
	public boolean traverseCompleted(int[][] A) {     //通过图总权重数判断是否完成了遍历
		int sum=0;
		   for(int i=0;i<A.length;i++) 
				for(int j=0;j<A[0].length;j++) 
					sum+=A[i][j];
		   if(sum==(1+A.length*A[0].length)*A.length*A[0].length/2)   //如果sum=1+2+...+N*M,则完成遍历
			       return true;	   
		   else 
			   return false;
	}	
	
	public static void main(String[] args) {
		Solution solution=new Solution();
		solution.solveKnightTravel(3,4);
	}
}

3*3的结果演示如下

 7  2  5 
 4  0  8 
 1  6  3 
false
 3  8  5 
 6  0  2 
 1  4  7 
false
一共有0种不同的遍历方法
一共有2次失败的尝试



Knight's Tour骑士游历问题(C语言实现)

#include int chessboard[8][8]; bool FindPath(int startI,int startJ) { //走法的序号 int queueNumber; ...
  • code_xbug
  • code_xbug
  • 2015-01-30 10:41:31
  • 545

POJ2488 A Knight's Journey(深搜DFS,字典序,骑士游历问题)

题目: A Knight's Journey Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4298...
  • riba2534
  • riba2534
  • 2017-01-07 17:35:59
  • 927

Knight Tour 骑士走棋盘算法(附代码)

骑士的走法为国际象棋的走法,骑士可以由任何一个位置出发,要求走完所有的位置。 基本情况下是可以使用递归,但是在递归维度大的时候,时间复杂度很高,效率很低。 下面介绍一个聪明的解法:...
  • Chuck_0430
  • Chuck_0430
  • 2013-08-24 22:04:13
  • 3093

POJ2488-A Knight's Journey【骑士游历】

转载请注明出处:優YoU http://user.qzone.qq.com/289065406/blog/1303350143  大致题意: 给出一个国际棋盘的大小,判断马能否不重复的走过所有格,...
  • lyy289065406
  • lyy289065406
  • 2011-07-31 01:11:38
  • 12513

The knight\'s tour(马周游问题)

三个策略:1、先从中心点开始走;2、往靠边走;3、对下一步进行评分,低分的先走。/*马周游问题,m*n的棋盘,放置在其上的马能否恰好访问每一个方格一次并回到起始位置深度优先搜索,若寻找到满足要求的解,...
  • zhihang1103
  • zhihang1103
  • 2013-09-26 21:37:30
  • 921

骑士游历算法

骑士游历,算法
  • sb___itfk
  • sb___itfk
  • 2016-03-16 15:26:18
  • 2287

acm题目及我的程序(2)——Knight Moves (骑士跳跃)

acm题目,来源 http://acm.zju.edu.cn/show_problem.php?pid=1091 problem statementA friend of you is doing r...
  • livelylittlefish
  • livelylittlefish
  • 2008-02-20 00:35:00
  • 8241

贪心算法解决骑士游历问题(C语言版)

  • 2012年12月13日 20:15
  • 700KB
  • 下载

每天一算法(骑士走棋盘)

关于骑士走棋盘问题,,一是不了解走棋盘的规则,二是不了解所要使用的那种由J.C. Warnsdorff在1823年提出的解法的意思是什么。。所以只能读代码,,但还真读懂了人家的代码,并自己实现了一下。...
  • Heaven13483
  • Heaven13483
  • 2012-12-11 15:58:41
  • 2456

AYITACM2016省赛第一周(深搜) E - A Knight's Journey骑士的旅行

E - A Knight's Journey Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u...
  • linyuxilu
  • linyuxilu
  • 2016-04-18 21:43:17
  • 467
收藏助手
不良信息举报
您举报文章:[易读易懂] 骑士游历算法 Knight's Tour Problem
举报原因:
原因补充:

(最多只允许输入30个字)