`

关于棋盘马走“日”字问题的回溯算法实现(分享以前做过的题)

阅读更多
1. 回溯法基本思想

   回溯法是在包含问题的所有解得解空间树(或森林)中,按照深度优先的策略,从根结点出发搜索解空间树。
算法搜索至解空间树的任一结点时,总是先判断该结点是否满足问题的约束条件。如果满足进入该子树,继续
按照深度优先的策略进行搜索。否则,不去搜索以该结点为根的子树,而是逐层向其祖先结点回溯。

   回溯法在用来求解问题的所有解时,要回溯到根,且根结点的所有可行的子树都已被搜索遍才结束。而回溯法
在用来求解问题的任一解时,只要搜索到问题的一个解就可以结束。适用于解决一些最优化问题。

2. 算法设计过程

(1) 确定问题的解空间
   
    应用回溯法解决问题时,首先应明确定义问题的解空间。问题的解空间应至少包含问题的一个最优解。

(2) 确定结点的扩展规则
   
     约束条件。

(3) 搜索解空间
  
    回溯算法从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也
成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并
成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应该往回
移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中
搜索,直至找到所要求的解或解空间中已没有活结点时为止。

3. 算法框架

(1) 问题框架

    设问题的解是一个n维向量(a1,a2,...,an),约束条件是ai(i1,2,...,n)之间满足某种条件,记为f(ai)。

(2) 非递归回溯框架

   int a[n], i;
   i=1;

   while(i>0(有路可走) and [未达到目标]){ //还未回溯到头
if(i>n){                          //搜索到叶结点
   搜索到一个解,输出;
        }else{
    a[i]第一个可能的值;
    while(a[i]不满足约束条件且在搜索空间内)
               a[i]下一可能的值;
            if(a[i]在搜索空间内){
标识占用的资源;
        i = i+1;                 //扩展下一个结点
    }else{
清理所占的状态空间;
                i = i-1;                 //回溯
    }
}
   }

(3)递归算法框架

   int a[n];
   try(int i){
if(i>n){
    输出结果;
}else{
    for(j=下界; j<=上界; j++){//枚举i所有可能的路径
if(f(j)){             //满足限界函数和约束条件
    a[i] = j;
                    ...               //其他操作
                    try(i+1);
                    a[i] = 0;         //回溯前的清理工作(如a[i]置空)
                }
    }
}
   }

4. 一个例子

  (1) 问题描述
     
      马的遍历问题。
      在n*m的棋盘中,马只能走"日"字。马从位置(x,y)出发,把棋盘的每一格都走一次且只走一次。找出所有路径。

  (2) 问题分析

      马是在棋盘的点上行走的,所以这里的棋盘是指行有N条边、列有M条边。而一个马在不出边界的情况下有8个方向
可以行走(走"日"字),如当前坐标为(x,y),则行走后的坐标可以为:
     (x+1, y+2)
     (x+1, y-2)
     (x+2, y+1)
     (x+2, y-1)
     (x-1, y-2)
     (x-1, y+2)
     (x-2, y-1)
     (x-2, y+1)

     搜索的解空间是整个棋盘上的n*m个点。
     约束条件是不出边界且每个点只经过一次。
     搜索过程是从任一点(x,y)出发,按照深度优先的原则,从8个方向中尝试一个可以走的点,直到走过棋盘上所有
n*m个点。

5. Java代码实现

package test;

/**
 * create on 2010.05.21 TODO 回溯算法
 * 
 * @author 毛正吉
 * @version v1.0
 * 
 */
public class RecollectionSearch {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// 注意(0<=x<n && 0<=y<m)
		int n = 5;
		int m = 4;
		int x = 0;
		int y = 0;
		RecollectionSearch rs = new RecollectionSearch(n, m, x, y);
		rs.find(x, y, 2);
		System.out.println("######################");
		System.out.println("总解数count=" + rs.getCount());
		System.out.println("######################");

	}

	// 棋盘行数
	private int n;
	// 棋盘列数
	private int m;
	// 马的起始x坐标
	private int x;
	// 马的起始y坐标
	private int y;
	// 棋盘坐标
	private int[][] a;
	// 求解总数
	private int count;
	// "日"子x坐标
	public int[] fx = { 1, 2, 2, 1, -1, -2, -2, -1 };
	// "日"子y坐标
	public int[] fy = { 2, 1, -1, -2, -2, -1, 1, 2 };

	/**
	 * 构造方法
	 * 
	 * @param _n
	 * @param _m
	 * @param _x
	 * @param _y
	 */
	public RecollectionSearch(int _n, int _m, int _x, int _y) {
		n = _n;
		m = _m;
		x = _x;
		y = _y;
		a = new int[n][m];
		for (int i = 0; i < n; i++) {
			for (int j = 0; j < m; j++) {
				a[i][j] = 0;
			}
		}
		// 马的起点
		a[x][y] = 1;
		count = 0;
	}

	public void find(int x, int y, int dep) {
		int i, xx, yy;
		for (i = 0; i < 7; i++) {
			xx = x + fx[i];
			yy = y + fy[i];
			// 判断新坐标是否出界,是否已走过
			if (check(xx, yy) == 1) {
				a[xx][yy] = dep;
				if (dep == n * m) {
					output();
				} else {
					// 从新坐标出发,递归下一层
					find(xx, yy, dep + 1);
				}
				// 回溯,恢复未走标志
				a[xx][yy] = 0;
			}
		}
	}

	/**
	 * 判断新坐标是否出界,是否已走过
	 * 
	 * @param xx
	 * @param yy
	 * @return
	 */
	public int check(int xx, int yy) {
		if (xx >= n || yy >= m || xx < 0 || yy < 0 || a[xx][yy] != 0) {
			return 0;
		}
		return 1;
	}

	/**
	 * 输出结果
	 */
	public void output() {
		count++;
		System.out.println("count=" + count);
		for (int y = 0; y < n; y++) {
			System.out.println("");
			for (int x = 0; x < m; x++) {
				System.out.print(a[y][x] + " ");
			}
		}
		System.out.println("");
	}

	public int getN() {
		return n;
	}

	public void setN(int n) {
		this.n = n;
	}

	public int getM() {
		return m;
	}

	public void setM(int m) {
		this.m = m;
	}

	public int getCount() {
		return count;
	}

	public void setCount(int count) {
		this.count = count;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
}

  
     
分享到:
评论
2 楼 maozj 2010-05-24  
crazysky 写道
第72行代码        for (i = 0; i < 7; i++) { 

这里的i < 8才对吧?不是有8个方向吗?

恩~~ 您说的对 应该是第72行代码        for (i = 0; i < 8; i++) {  谢谢
1 楼 crazysky 2010-05-23  
第72行代码        for (i = 0; i < 7; i++) { 

这里的i < 8才对吧?不是有8个方向吗?

相关推荐

    关于马走日棋盘搜索算法

    这里我们将深入探讨马走日算法的概念、实现以及在VC++6.0和Windows XP环境下如何进行调试。 首先,我们要理解马走日的移动规则。在8x8的标准棋盘上,马每次可以向前或向后跳跃两格,然后向左或向右跳一格,或者相反...

    马走日棋盘算法.doc

    马走日棋盘算法是解决在给定大小的方格状棋盘上,棋子”马”从指定的起始位置开始,走遍棋盘上的所有落子点的算法。该算法需要搜索一条可行的路径,使得棋子”马”能走遍棋盘上的所有落子点,每个落子点只能走一次。...

    回溯算法的C++实现

    回溯算法是一种试探性的解决问题的方法,它尝试逐步构建解决方案,并在发现某一步无法达到期望结果时,采取撤销或“回溯”先前的选择,去尝试其他可能的路径。这种算法广泛应用于解决约束满足问题,如图着色、旅行商...

    在n x n棋盘(有n x n个格点的棋盘)的某个格点上有一个中国象棋马,马走日字。求一条周游棋盘的路径,使得马能够从起始位置起沿着该路径每个格点恰好走一次最后回到出发位置。 用回溯法解决该问题。输入一个正整数n,输出一个解,解的输出形式尽可能直观。

    在n x n棋盘(有n x n个格点的棋盘)的某个格点上有一个中国象棋马,马走日字。求一条周游棋盘的路径,使得马能够从起始位置起沿着该路径每个格点恰好走一次最后回到出发位置。 用回溯法解决该问题。输入一个正整数n...

    回溯算法例题,C++实现

    回溯算法是一种在解决问题时,通过尝试所有可能的解决方案,并在每一步中排除那些不可能产生正确结果的选项来寻找问题答案的方法。它主要用于解决约束满足问题,如迷宫求解、八皇后问题、数独填充等。在本例题中,...

    VC 中国象棋棋盘马走日算法.rar

    《VC 中国象棋棋盘马走日算法详解》 中国象棋,作为深受中国人民喜爱的传统棋类游戏,其规则独特且富有策略性。在计算机编程领域,实现中国象棋的算法是一项挑战,尤其是马走日这一特殊的移动规则。本文将深入探讨...

    马走棋盘的实现

    在IT领域,编程和算法是核心技能之一,而“马走棋盘”是一个经典的计算机科学问题,它涉及到递归算法的运用。递归是一种解决问题的方法,它将问题分解为更小的子问题,直到子问题变得足够简单可以直接解决。在这个...

    n后问题回溯算法 java

    总结起来,n皇后问题通过回溯算法在Java中的实现,不仅锻炼了我们的逻辑思维能力,也让我们更好地理解了递归和回溯这两种重要的编程技巧。同时,这个问题也为我们提供了一个在有限搜索空间中寻找解的有效方法,这在...

    回溯算法实现5皇后问题

    回溯算法是一种试探性的解决问题的方法,它尝试逐步构建解决方案,并在每一步检查当前解是否可行。如果当前解不可行,算法会撤销上一步的操作并尝试其他的可能性,直到找到所有可能的解决方案或确定不存在解决方案...

    马的遍历/骑士问题 回溯算法 算法设计作业

    马的遍历,骑士问题,马踏棋盘。回溯算法的经典问题,还有八皇后等。马的遍历也是一个。上算法课正好有这个问题,找了下能用的,vc++6.0调试可用

    算法设计与分析马踏棋盘问题实现

    马踏棋盘问题是一个经典的计算机科学问题,它涉及到在一个N×N的棋盘上,通过一匹虚拟的“马”按照中国象棋中的马走日规则(即两横一竖或两竖一横),尝试访问棋盘上的每一个格子,并且每个格子只能被访问一次。...

    回溯算法——n后问题

    回溯算法是一种在解决问题时,通过尝试所有可能的解决方案,并在发现不符合条件的解时能够及时终止当前路径,退回一步并尝试其他可能的路径的方法。它通常用于解决约束满足问题和搜索问题,如迷宫问题、八皇后问题、...

    最简洁马走日c程序(回溯打印所有能走通路径)

    堪称史上最简单递归回溯马走日,看完课本后写的,看完代码会对递归回溯有更好的了解。

    067回溯算法的实现及实例_回溯算法实现实例_

    回溯算法是一种强大的问题求解方法,常用于解决组合优化问题和搜索问题。它通过尝试所有可能的解决方案路径,一旦发现某个路径无法达到目标,就会撤销该路径的选择,退回上一步,继续探索其他可能性,直到找到一个...

    C语言/c++解决马走日问题。:从(0,0)点到(x,y)点,只能往右,输出每种路径总数并打印出各种路径

    C语言/c++马走日问题1)问题描述。马从(0,0)出发,只能往右(右上或右下)跳,从(0,0)点到(8,4)点,这个区域内有多少种不同的路径,并打印出各种路径。 压缩包里有c语言的和c++语言的。 本程序可输入任意的...

    acm竞赛回溯算法总结

    回溯算法是一种试探性的解决问题的方法,它在搜索解空间树的过程中,通过不断地尝试来寻找问题的解。在ACM(国际大学生程序设计竞赛)中,回溯算法常常被用来解决那些具有大量可能解且需要尝试多种路径的问题。下面...

    棋盘覆盖算法实现

    棋盘覆盖算法是一种经典的计算机科学问题,主要涉及图论、组合优化和算法设计。它源自一个实际的棋盘游戏,但被广泛应用于数据结构优化、网络设计和编码等多个领域。在本例中,我们讨论的是C++实现的棋盘覆盖算法。 ...

    马踏棋盘的贪心算法实现

    在马踏棋盘问题中,贪心算法可能的具体实现步骤如下: 1. **初始化**:设定一个空的访问数组,用来标记棋盘上的每个格子是否已被马访问过。同时,定义起点,通常为棋盘的左上角,并标记为已访问。 2. **贪心策略**...

    Java基于分治算法实现的棋盘覆盖问题示例

    本文主要介绍了Java基于分治算法实现的棋盘覆盖问题,简单描述了棋盘覆盖问题,并结合具体实例形式分析了Java基于分治算法实现棋盘覆盖问题的相关操作技巧。 知识点一:分治算法的基本概念 分治算法是一种将复杂...

Global site tag (gtag.js) - Google Analytics