跳马问题、骑士遍历问题
骑士游历问题(C语言代码)
骑⼠游历问题(C语⾔代码)关于骑⼠游历问题,⼤家可以想到的⽅法是回溯法和贪⼼算法。
回溯法的时间复杂度⽐较⾼,贪⼼算法的时间复杂度就好多了。
骑⼠游历问题问题描述:棋盘⼤⼩是8*8,骑⼠在棋盘任⼀⽅格开始游历。
要求骑⼠游历棋盘的每⼀个⽅格且每个⽅格只游历⼀次。
输出骑⼠的游历路径。
解决思路:dir0、dir1…dir7由⼩到⼤排列,每次选择具有最少出⼝的⽅向,假设为dir0。
如果骑⼠⾛到第n步时,没有出⼝可以选择,同时,骑⼠未完全遍历棋盘,那么骑⼠回溯到上⼀位置((n-1)步),选择dir1⽅向。
骑⼠游历依次进⾏下去。
直⾄⾛到终点或回到原点。
终点代表骑⼠游历完成,原点代表骑⼠游历失败(没有可以游历每个⽅格游历且只游历⼀次的路径)。
这⾥假设当前位置的⼋个⽅向已经按照“具有出⼝”由⼩到⼤排列。
编写程序过程要解决的问题(先考虑简单的问题):⼀、每个位置最多有8个⽅向可以移动分别为:(-2,1) (-1,2) (1,2) (2,1) (2,-1) (1,-2) (-1,-2) (-2,-1);主函数中骑⼠在初始位置时,对个变量进⾏初始化代码:step=1;chessboard[startx][starty]=step;step++;cur_x=startx;cur_y=starty;dir[startx][starty]=max_dir;last_dir=max_dir;考虑到解决骑⼠游历需要⽤到多个函数。
骑⼠前进、后退函数可能⽤到这⼋个⽅向移动的坐标。
故将⼋个⽅向移动的坐标定义为全局变量,“intktmove_x[max_dir],ktmove_y[max_dir];”。
⼆、判断骑⼠应该前进还是回溯:骑⼠在当前位置有可移动的⽅向则前进,反之,回溯到上⼀位置。
⼋⼋棋盘上⾯的每个⽅格最多有⼋个⽅向可以移动。
棋盘数组:“int chessboard[width][width];”,则可以定义⼀个三维数组来判断⽅格某⼀⽅向是否被访问过:“int is_visted[width][width][max_dir]={0};”,初始化为0,表⽰64个⽅格具有的出⼝⽅向(最多有⼋个)未被访问,两个数组同样是全局变量。
骑士周游问题
回专题模式回学习阶段模式【题目名称、来源】骑士周游问题(经典题目knight.pas)【问题描述】骑士周游问题如下图所示,国际象棋棋盘上的马一步可以跳向八个方向,问:如果给出一个n*m的棋盘,马从左上角开始跳,是否能够不重复的跳遍所有的格子。
输入:棋盘大小n m列,便利棋盘格子的顺序源程序:【所属专题】【适合学习阶段】【解题思路】问题分析:存储结构:constdirect:array[1..8,1..2] of integer=((-2,1),(-1,2),(2,1),(1,2),(2,-1),(1,-2),(-1,-2),(-2,-1));{马可以跳向八个方向的相对位移,使用常量数组可以将规则统一}Vara:array[1..10,1..10] of integer;{使用数组作为棋盘的存储,如果某一格子没有走过值为0,否则为步数}【测试数据】【源程序】program qishi;{Qi shi zhou you wen ti}constdirect:array[1..8,1..2] of integer=((-2,1),(-1,2),(2,1),(1,2),(2,-1),(1,-2),(-1,-2),(-2,-1));{马可以跳向八个方向的相对位移,使用常量数组可以将规则统一} var n,m,x,y:integer;i,j,count:integer;a:array[1..10,1..10] of integer;procedure tiao(step,x,y:integer);{递归过程实现回溯}var r,i,j,k,l:integer;{r是规则号,i,j是按照r规则跳后的新位置} beginfor r:=1 to 8 do begini:=x+direct[r,1];j:=y+direct[r,2];{求新位置}if (i>=1)and(i<=n)and(j<=m)and(j>=1) then {如果新位置合法}if a[i,j]=0 then begina[i,j]:=step;if step=n*m then begin{如果到达终点则打印}for k:=1 to n do beginfor l:=1 to m do write(a[k,l]:3);writeln;end;close(output);halt;endelse tiao(step+1,i,j);{如果没有到终点,跳下一步}a[i,j]:=0;{回溯}end;end;end;beginassign(input,'qishi.in');reset(input);readln(n,m);{init}assign(output,'qishi.out');rewrite(output);for i:=1 to n dofor j:=1 to m do a[i,j]:=0; {初始化数组}a[1,1]:=1;tiao(2,1,1);writeln('Impossible');close(output);end.。
跳马问题中存在的多种算法思想
跳马问题中存在的多种算法思想作者:邓立波来源:《中国信息技术教育》2012年第01期在信息量飞速增长的现代社会中,如何有效地获取和处理信息已成了信息技术教师面对的课题之一。
如何有条理地分析各种问题中的有用数据,发现数据的变化规律,制订相应的处理方法,构建解决整个问题的流程,也成了教师们的日常工作之一。
众所周知,计算机的特征是运算速度快、精度高,在精确的控制之下,一切都会按部就班地执行,从而提高效率。
如果我们能很好地利用计算机这个工具,那么我们就只需做个规划者,而繁琐的处理过程就可以留给计算机来执行。
下面我就以对一个问题的分析来说明流程控制中的多样性。
问题描述:设一个m*n的棋盘,在棋盘上左下角的A点(0,0)有一个中国象棋的马,并约定马走的规则是:①马走日字;②马只能向右走。
任务:编程读入m,n,请打印出一条从A(0,0)到B(m,n)的路径。
跳马问题也称为骑士遍历问题,是一道非常古老的题目了,好多人在讲回溯算法时都喜欢用到这个例子,因为这是适合用回溯法解决的一个经典例子,它仅用一张平面表格就把阶段、状态、决策等因素直观而又抽象地展现在人们面前。
但我们不希望仅以一道或几道经典题目,让学生掌握一种算法,而是从不同的角度、方向来分析当前这种变化规则,找到适合的方法来处理目前这种变化,从而提高学生的解题适应能力。
● 回溯法适用于解决多阶段决策问题中求单路径、全路径等用回溯法解决跳马问题的基本思想是从起点开始每一步先作出选择,再判断,再跨出到下一步,重复上述过程,每一步的跨出都会离目的地越来越近,但当到不了目的地时,回退一步,如果可以作出新的选择,则重新跨出,否则再退,以此类推。
上述过程可以借助于图形进行模拟,让学生先清楚整个过程。
接下来便是每一步骤的语言描述及控制。
其中每一步所做出的选择必须记录,否则回退时不能做出新的选择。
求单路径的流程图如下:参考程序:program knight1;const dx:array[1..4] of integer=(1,2,2,1);dy:array[1..4] of integer=(-2,-1,1,2);max=1000;var x,y,m,k,i,mm,nn:integer;b:array[0..max] of integer;beginwrite('input m,n:');readln(mm,nn);x:=0;y:=0;m:=0;k:=0;for i:=0 to max do b[i]:=0;while (xmm) or (ynn) do {目标点为(mm,nn)}begink:=k+1; {找个方向}if k>4 then begin k:=b[m]; {没有方向可跳,则回溯} m:=m-1;x:=x-dx[k];y:=y-dy[k];endelseif (x+dx[k]=0) or (y+dy[k]begin {继续往下跳}m:=m+1;b[m]:=k;k:=0;end;end;for i:=1 to m do write(b[i]:9); {输出每步的方向}writeln;x:=0;y:=0; {输出路径}write('(0,0)--->');for i:=1 to m dobeginx:=x+dx[b[i]];y:=y+dy[b[i]];if im then write('(',x,',',y,')','--->')else writeln('(',x,',',y,')');end;readln;end.以上是求从起点到终点的一条路径,这是一种能深入先深入,否则回退,换条路走的思维方式,即深度优先搜索。
马踏棋盘算法
马踏棋盘算法介绍1.马踏棋盘算法,也称骑⼠周游问题2.规则:将马随机放在国际象棋的 8×8 棋盘[0~7][0~7]的某个⽅格中,马按⾛棋规则(马⾛⽇字)进⾏移动,要求每个⽅格只进⼊⼀次,⾛遍棋盘上全部 64 个⽅格3.基本思想:图的深度优先搜索 + 贪⼼算法(优化)步骤1.创建⼀个⼆维数组代表棋盘2.将当前位置设置为已经访问,把当前马在棋盘的位置标记为第⼏步(step),然后根据当前位置,计算马还能⾛哪些位置,并放⼊ArrayList,最多有8个位置3.获取到ArrayList后,⽤贪⼼算法优化4.遍历ArrayList中存放的所有位置,判断哪个位置可以⾛通,如果⾛通,就继续,⾛不通,就回溯5.判断是否⾛完整个棋盘,使⽤step和应该⾛的步数⽐较,如果没有达到数量,则表⽰没有完成任务,并将整个棋盘置0贪⼼算法优化ArrayList1.基本思想:根据当前这个⼀步的所有的下⼀步的选择位置,进⾏⾮递减(即不严格递增)排序,减少回溯的次数步骤(1)传⼊Comparotor,重写ArrayList的sort⽅法(2)获取ArrayList所有位置的通路个数,按⾮递减排序(3)即当前位置的下⼀步的下⼀步通路最少,就优先选择代码实现import java.awt.*;import java.util.ArrayList;import java.util.Arrays;import parator;public class KnightTravel {//骑⼠周游问题public int row;//棋盘的⾏数public int column;//棋盘的列数public int[][] chessboard;//棋盘public boolean[][] visited;//标记棋盘的各个位置是否被访问过,true:已访问public boolean finished;//标记是否棋盘的所有位置都被访问,若为true,表⽰算法计算成功public int step = 1;//初始为第⼀步public static void main(String[] args) {KnightTravel knight = new KnightTravel(8, 8);long start = System.currentTimeMillis();knight.travel(knight.chessboard, 0, 0, knight.step);long end = System.currentTimeMillis();System.out.println("共耗时: " + (end - start) + " 毫秒");knight.result();}public KnightTravel(int row, int column) {this.row = row;this.column = column;this.chessboard = new int[row][column];this.visited = new boolean[row][column];}public void result() {//打印算法结果for (int[] row : chessboard) {System.out.println(Arrays.toString(row));}}//马踏棋盘算法//chessboard:棋盘,row:马⼉当前的位置的⾏,从0开始,column:马⼉当前的位置的列,从0开始,step:第⼏步,初始位置是第1步 public void travel(int[][] chessboard, int row, int column, int step) {chessboard[row][column] = step;visited[row][column] = true; //标记该位置已经访问//获取当前位置可以⾛的下⼀个位置的集合ArrayList<Point> access = next(new Point(column, row));sort(access);//对access进⾏⾮递减排序while (!access.isEmpty()) {Point next = access.remove(0);//取出下⼀个可以⾛的位置if (!visited[next.y][next.x]) {//判断该点是否已经访问过travel(chessboard, next.y, next.x, step + 1);}}if (step < this.row * this.column && !finished) {//⽆路可⾛且未⾛完,就回溯到上⼀位置chessboard[row][column] = 0;visited[row][column] = false;} else {//成功⾛完时,finished是防⽌回溯时将最优解覆盖掉finished = true;}}//根据当前位置(curPoint),计算马还能⾛哪些位置(point),并放⼊⼀个集合中(ArrayList), 最多有8个位置public ArrayList<Point> next(Point curPoint) {//储存马在当前位置的通路ArrayList<Point> access = new ArrayList<>();//Point对象中,属性x代表列,属性y代表⾏Point point = new Point();//以下操作,判断马的8个⽅向是否可⾏,并记录通路位置,但不记录该位置是否已访问if ((point.x = curPoint.x - 2) >= 0 && (point.y = curPoint.y - 1) >= 0) {access.add(new Point(point));}if ((point.x = curPoint.x - 1) >= 0 && (point.y = curPoint.y - 2) >= 0) {access.add(new Point(point));}if ((point.x = curPoint.x + 1) < column && (point.y = curPoint.y - 2) >= 0) {access.add(new Point(point));}if ((point.x = curPoint.x + 2) < column && (point.y = curPoint.y - 1) >= 0) {access.add(new Point(point));}if ((point.x = curPoint.x + 2) < column && (point.y = curPoint.y + 1) < row) {access.add(new Point(point));}if ((point.x = curPoint.x + 1) < column && (point.y = curPoint.y + 2) < row) {access.add(new Point(point));}if ((point.x = curPoint.x - 1) >= 0 && (point.y = curPoint.y + 2) < row) {access.add(new Point(point));}if ((point.x = curPoint.x - 2) >= 0 && (point.y = curPoint.y + 1) < row) {access.add(new Point(point));}return access;}//根据当前这个⼀步的所有的下⼀步的选择位置,进⾏⾮递减(不严格递增)排序, 减少回溯的次数public void sort(ArrayList<Point> access) {access.sort(new Comparator<Point>() {@Overridepublic int compare(Point o1, Point o2) {int count1 = next(o1).size();int count2 = next(o2).size();if (count1 < count2) {return -1;} else if (count1 == count2) {return 0;} else {return 1;}}});}}。
骑士遍历问题
| 返回主页 | 本站地图 | 站内搜索 | 联系信箱 |您目前的位置:首页 > 自由软件 > 技术交流 > 应用编程蓝森林 2006年6月6日 10:18关于马踏棋盘问题的求教最近看了马踏棋盘问题,突然比较感兴趣,写了段程序,主要思路是这样的/*Analyse:1.init chessboard2.start from (0.0) and try direction from 03.if can_go,go next;if can't,try another direction4.if have try all the direction, go back old one and init the current point5.if try all the direction of start point(0.0),return can't find, otherwise print the chessboard*/突然想深入了解这个问题,对于n*n 的棋盘,求共有多少种走法.要求用C 来实现,不知道大家有什么想法.谢谢.关于马踏棋盘问题的求教本人新手,在此块讨论区人微言轻,本不敢谈什么个人的看法.但我以前写过类似的程序,我做的题是8*8,选择任意点为起点马踏完成,记录每一步.从理论上来说用递归算法是可以的,但是在TC 环境下是得不出答案的 因为递归后,一步一步尝试使递归层越陷越深,不知道怎么的就死了(哪位能告诉我什么原因吗?)最后我找到一种方法就是不论以哪个点为始点,我都向外围靠,走完外圈再走内圈所以不论哪个点为始点都能走通.至于N*N还没有尝试过.问一下你的程序运行了吗?关于马踏棋盘问题的求教深度优先的递归遍历适于求解那些简单问题,因为情况复杂之后,递归深度太大,将造成堆栈耗尽。
比较复杂的问题适于采用广度优先的遍历。
如果情况进一步复杂,还可以在广度节点扩展的时候加入启发函数,进行a*遍历。
跳马问题中存在的多种算法思想
这 是适合 用回溯法 解决 的一个 经典 例 子, 它仅用一张 平面表 格就 把阶段 、 状 态 、 策等 因素 直 观 而又抽 象 地 展 现 决
在人 们面 前。 但我们不希望仅以一道 或 几 道 经典题 目, 学生 掌握一 种算法 , 让 输出每步的决策
是否到终点
下步的初始化
而是 从不 同的角度、 向来分析 当前 这 方 种变 化规 则, 找到适 合的方法来处 理目 前这 种 变化 , 而 提高 学 生的解 题 适 从
设一个m n ,的棋 盘, 在棋盘上左下角 的A (,) 点 00 有一个 中国象棋 的马, 并约定
5课堂上 , 信息技术 新旧知识 . 注重 之 间的衔 接 , 引导学 生温 故 而知新 , 利 用知识的时间和空间的迁 移, 善于 用学
表2 《 数据处理》 价表 评
很棒 项 目 具 体 袭 现
初始化
( 0到B m n的路 径。 0 ) (,) ,
~ —
选择状 态
跳 马 问题 也 称 为骑 士 遍 历 问题 , 是一道 非常古 老的题 目 好 多人 在讲 了,
回溯算法 时都喜欢 用到这个 例子 , 因为
-~ — —
竺二——/ —— ! —/
记录当前决策
回退一步 , 记录上 步决策
的规 模出发 , 不同规模的问题解 决方案 是否一致, 能否找到大规模 问题与小规 模问题 ( 子问题 ) 间的关系, 并建立递 归 模型 。 题在每 一步上 存在四种选择 , 本
el e s
多少, 是否一定要用上 述方法求出所有
y v o0] : +y i = 4] ;
i i > m then f <
出 步 每 的方向 }
回溯算法
三、回溯的一般步骤
回溯法正是针对这类问题,利用这类问题的
上述性质而提出来的比枚举法效率更高的算 法。
二、回溯的一般描述
procedure rbacktrack(k); begin if k > n then return else for each x(k),如果x(k)∈t(x(1)…x(k-1))且 b(x(1)…x(k))=true do begin if x(1)…x(k)是一个解 then write(x(1)…x(k) else rbacktrack(k+1); end; end;
演示
一、回溯的概念
像走迷宫这样,遇到死路就回头的搜索思路
就叫做“回溯”。
从问题的某种可能情况出发,搜索所有能到
达的可能情况,然后以其中一种可能的情况 为新的出发点,继续向下探索,当所有可能 情况都探索过且都无法到达目标的时候,再 回退到上一个出发点,继续探索另一个可能 情况,这种不断回头寻找目标的方法称为 “回溯法”。
二、回溯的一般描述
可用回溯法求解的问题P,通常要能表达为:
对于已知的由n元组(x1,x2,…,xn)组成 的一个状态空间E={(x1,x2,…,xn) ∣xi∈Si ,i=1,2,…,n},给定关于n元组 中的一个分量的一个约束集D,要求E中满足 D的全部约束条件的所有n元组。其中Si是分 量xi的定义域,且 |Si| 有限,i=1,2,…, n。我们称E中满足D的全部约束条件的任一 n元组为问题P的一个解。
骑士遍历
骑士遍历问题的解空间是从左下角到右上角
node、扩展节点)。 从E-节点可移动到一个新节点。 如果能从当前的E-节点移动到一个新节点,那么这个新 节点将变成一个活节点和新的E-节点,旧的E-节点仍是 一个活节点。 如果不能移到一个新节点,当前的E-节点就“死”了 (即不再是一个活节点),那么便只能返回到最近被考 察的活节点(回溯),这个活节点变成了新的E-节点。 当我们已经找到了答案或者回溯尽了所有的活节点时, 搜索过程结束。
骑士漫游问题
break;
case 4 :
tempx=x+2;
tempy=y+1;
case 1 :
tempx=x+1;
tempy=y-2;
break;
case 2 :
int methodOrder[];
public Knight(int x,int y,int accessiblity[][]) { //构造骑士棋子,位置是(X,Y),访问能力数组accessiblity
this.x=x;
对此进行进一步讨论,此方法存在大量回溯,时间代价非常大,在8*8的棋盘上很可能在可容忍的时间类无法找到路径,所以对起进行优化。考虑棋盘,直观的,按骑士走法,中间最容易被访问,边角最难以访问到。进一步考虑棋盘每一格,按骑士走法,能访问到此格的上一格的个数是不同的,把能访问此格的上一格的个数之和称为每格的访问能力。所以优化方法为:尽量先访问访问能力低的格子。为此给棋盘添加一个N*N的二维数组,表示每格的访问能力值。给棋子添加一个8长度的数组,用来表示优先访问顺序。计算每格的访问能力算法:从棋盘上的每格出发,向八个方向进行尝试,若没有超出边界,则此格访问能力加1。优先访问顺序算法:重当前位置想八个方向试探,记录八个位置,比较访问能力数组,找到八个位置的访问能力,若越界,则需要最后访问,所以访问能力可以设为9,最后对八个位置按访问能力进行排序,得到优先访问顺序。
top=-1;
length=0;
}
public boolean isEmpty(){
return this.top==-1;
}
public boolean push(E element){
国际象棋跳马问题(Knight'sTours)算法实现
Warnsdorff(0, 0, panel); for(i=0; i<panel.length; i++){
for (j=0; j<panel[i].length; j++){ if (panel[i][j]==0) none++; System.out.print((panel[i][j]>=10)?(panel[i][j]+" "):(panel[i][j]+" "));
} return count; } public static void Warnsdorff(int m, int n, int[][] arr){ int i, j, step = 2, count = 8, subcount, iok = 0, jok = 0; boolean find = false;
跳马问题的解决方法
在跳马问题的研究方面,欧拉(Euler)是最早对其进行系统研究的少数数学家之一,并且, 他最早创建算法解决了跳马问题。欧拉的解决方法是通过整合较小和不完全路径来创建新的 路径(数学家 L.Bertrand 认为)(请参阅 Introducing Knight's Tours by Jellis,George)。 最经典的解决跳马问题的算法是德国数学家 H.C. Warnsdorff 在 1823 年发明的算法,称作 Warnsdorff 规则(请参阅 Warnsdorff' Rule by Trnberg,Gunno)。Warnsdorff 算法描述为 在跳马是总是跳到具有最少合法下级跳动路径的格内,也就是说,当本次跳动有几个合法路 径(合法方格)可以选择时,我们选择是具有最少的下级合法方格的那个方格。这样就很好 地避免了跳马过程中的死节点(死路径)问题(死路径:棋盘没有跳满,单本格的所有下级 合法方格已经跳过)。譬如我们吃葡萄,如果有一大堆葡萄要求我们都把他吃完,但是葡萄 太多我们有可能吃不完,如果我们要尽量把他吃完应该怎么办呢?一种好的解决方法就是先 拣不好吃的吃,这样越吃越好吃,最后有可能全部吃完。Warnsdorff 算法并不是对所有情 况都是用,当棋盘大于 76×76 时,这种算法就无效了。Arnd Roth 也为跳马问题提出了较 好的算法,他的算法思想是每次选择下级合法方格时,保证所选合法方格是所有合法方格中 距离棋盘中心最远的。Arnd Roth 的算法也有效地解决了跳马问题,但是实验证明,当棋盘 大小大于 428×428 时也失败了。
回溯算法原理和几个常用的算法实例
回溯算法思想:回溯(backtracking)是一种系统地搜索问题解答的方法。
为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。
下一步是组织解空间以便它能被容易地搜索。
典型的组织方法是图(迷宫问题)或树(N皇后问题)。
一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。
回溯方法的步骤如下:1) 定义一个解空间,它包含问题的解。
2) 用适于搜索的方式组织该空间。
3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。
回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。
在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。
因此,回溯算法的空间需求为O (从开始节点起最长路径的长度)。
这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。
所以如果要存储全部解空间的话,再多的空间也不够用。
算法应用:回溯算法的求解过程实质上是一个先序遍历一棵"状态树"的过程,只是这棵树不是遍历前预先建立的,而是隐含在遍历过程中。
(1) 幂集问题(组合问题)(参见《数据结构》(严蔚敏))求含N个元素的集合的幂集。
如对于集合A={1,2,3},则A的幂集为p(A)={{1,2,3},{1,2},{1,3},{1},{2,3},{2},{3},Φ}幂集的每个元素是一个集合,它或是空集,或含集合A中的一个元素,或含A 中的两个元素,或者等于集合A。
反之,集合A中的每一个元素,它只有两种状态:属于幂集的元素集,或不属于幂集元素集。
则求幂集P(A)的元素的过程可看成是依次对集合A中元素进行“取”或“舍”的过程,并且可以用一棵状态树来表示。
求幂集元素的过程即为先序遍历这棵状态树的过程。
程序:#include <stdio.h>#include <malloc.h>#define ERROR 0#define OK 1typedef int ElemType;typedef struct LNode{ElemType data;struct LNode *next;} LNode,*LinkList;//初始化LinkList ListInit(){LNode *base=(LinkList)malloc(sizeof(LNode)); base->data=0;base->next=NULL;return base;}//插入一个元素int ListInsert(LinkList L,int i,ElemType e){LNode *p,*s;int j=0;p=(LNode *)L;while(p&&j<i-1){p=p->next;++j;}if(!p||j>i-1)return ERROR;s=(LNode *)malloc(sizeof(LNode));s->data=e;s->next=p->next;p->next=s;return OK;}//删除一个结点int ListDelete(LinkList &L,int i,ElemType &e) {LinkList p=L,q;int j=0;while(p->next&&j<i-1){p=p->next;++j;}if(!(p->next)||j>i-1)return ERROR;q=p->next;p->next=q->next;e=q->data;free(q);}//长度int ListLength(LinkList L){LinkList p=L;int j=0;if(!L)return ERROR;while(p->next){p=p->next;++j;}return j;}//查找一个元素int GetElem(LinkList L,int i,ElemType &e) {LNode *p=L;int j=0;while(p->next&&j<i){p=p->next;++j;}if(!p||j>i)return ERROR;e=p->data;return OK;}//输出链表元素void Display(LinkList L){LNode *p=L;if(!(p->next)){printf("NULL,");return;}elsep=p->next;while(p){printf("%d,",p->data);p=p->next;}}//求幂集void PowerSet(int i,LinkList A,LinkList &B) {int k=0;ElemType e=0;if(i>ListLength(A)){Display(B);printf("\n");}else{GetElem(A,i,e);k=ListLength(B);ListInsert(B,k+1,e);PowerSet(i+1,A,B);ListDelete(B,k+1,e);PowerSet(i+1,A,B);}}int main(){LinkList list=ListInit(); //初始化LinkList list2=ListInit();//初始化ListInsert(list,1,1);//插入元素ListInsert(list,2,2);ListInsert(list,3,3);Display(list);//输出元素printf("\npower set is:\n");PowerSet(1,list,list2);//求幂集}(2)迷宫问题(参见《数据结构》(严蔚敏))计算机解迷宫时,通常用的是"试探和回溯"的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止,如果所有可能的通路都试探过,还是不能走到终点,那就说明该迷宫不存在从起点到终点的通道。
骑士遍历问题代码
#include <iomanip>#include <iostream>using namespace std;enum boolean{TRUE = 1,FALSE = 0};const int MAX = 8;const int MAX_WIDTH = 30;class KNIGHT {public:KNIGHT(int width); //设置初始状态void print(); //打印问题的解boolean travel(int start_x, int start_y); //根据马的起始位置(start_x, start_y)使用回溯算法求骑士游历问题的一个解protected:void init_direction(); //初始化记录所选方向的数组,将每个值置为MAXvoid init_chessboard(); //初始化记录马在第几步到达每个位置的数组,将每个值置为0void set_start(int x, int y); // 设置初始状态,包括初始化方向数组和棋盘数组,并设置马的初始位置virtual boolean select_direction(); //在当前位置选择一个方向以推进到下一位置virtual void backward(); //从当前位置回溯到上一位置virtual void forward(); //从当前位置推进到下一位置boolean is_legal(int x, int y); //判断马是否能够走向位置(x, y)。
boolean back_to_start(); //判断是否回溯到初始状态boolean is_end(); //判断是否游历完所有位置// 下面两个数组用来记住选择某个方向后,推进到下一位置时x方向和y方向的值的变化int var_x[MAX];int var_y[MAX];int chessboard[MAX_WIDTH][MAX_WIDTH]; // 记录马第几步到达某个位置的棋盘数组int direction[MAX_WIDTH][MAX_WIDTH]; // 记录马在某个位置是在上一位置选择第几个方向到达的int width; // 棋盘宽度int curr_x, curr_y; // 马的当前位置int step; // 已经游历的步数int last_direction; // 上一位置到当前位置所选的方向};class FAST_KNIGHT: public KNIGHT{public:FAST_KNIGHT(int width);protected:void init_cannot(); //初始化cannot数组virtual boolean select_direction(); //在当前位置根据启发式规则选择一个方向以推进到下一位置virtual void backward(); //从当前位置回溯到上一位置,注意维持cannot数组virtual void forward(); //从当前位置根据所选择的方向推进到下一位置boolean cannot[MAX_WIDTH][MAX_WIDTH][MAX]; // 用来记住某个位置某个方向是否已经试探过};KNIGHT::KNIGHT(int width){this->width = width;init_direction();int total_step = 0;}boolean KNIGHT::is_legal(int x, int y){if (x < 0 || x >= width) return FALSE;if (y < 0 || y >= width) return FALSE;if (chessboard[x][y] > 0) return FALSE;return TRUE;}boolean KNIGHT::back_to_start(){if (step == 1) return TRUE;else return FALSE;}boolean KNIGHT::is_end(){if (step > width * width) return TRUE;else return FALSE;}void KNIGHT::set_start(int x, int y){curr_x = x;curr_y = y;step = 1;chessboard[x][y] = step;step = step + 1;direction[x][y] = MAX;last_direction = MAX;}boolean KNIGHT::select_direction(){int try_x, try_y;// last_direction为MAX表示当前位置是一个新的位置,在新推进到某个位置(curr_x, curr_y)时,// direction[curr_x][curr_y]会记录上一位置到(curr_x, curr_y)时所选择的方向,这时// last_direction置为MAX用来标记该位置是新推进的位置。
骑士巡游问题的回溯法分析
算法设计与分析课程论文骑士巡游问题的回溯法分析学院:信息工程学院姓名:学号:指导老师:问题描述:骑士巡游(knight's tour)问题是指在有8×8 方格的国际象棋棋盘上进行奇异的骑士“L 型”(L-shaped)移动的问题。
在国际象棋棋盘8×8 方格上的某个格子上放置一个骑士,然后这个骑士只能以马跳的方式前进,要求这个骑士相继地到达所有的64 个方格,进入每个方格一次且仅进入一次。
问题分析:“L型”移动:骑士的步进方式是按照“L型”移动的,即如下图所示,假设骑士的当前位于粉色格子的位置,那么它的下一步可能出现的合法位置为绿色格子的位置。
如此,我们定义坐标系,棋盘左上角格子为坐标原点(0,0),横坐标X轴以右为正方向,Y 轴以下为正方向,当前骑士位置为(x,y),则可能出现的位置为(x-2,y+1)、(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)。
如此,骑士没进一步都按照此方式步进,直至整个棋盘都被“游走”一遍则完成。
边界情况分析:在骑士“巡游”的过程中难免会游走到棋盘的边缘,那么此时下一步的坐标位置可能超出棋盘边界,此种情况下,需要相关的限定代码予以限制。
此外,因为要求棋盘每个位置要巡游且只巡游一次,所以当骑士巡游到某一位置时,可能会出现,棋盘没有被巡游完全,但不存在合法的下一步坐标点,此种情况下,则涉及到回溯的问题。
回溯算法的相关介绍:回溯法总述:回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
回溯法的深度优先搜索策略:回溯法的基本做法是搜索,或是一种组织得井井有的,能避免不必要搜索的穷举式搜索法。
应用举例跳马问题动画演示
向),那么我们很容易得到每一个方向上两 个坐标和原位置的关系。
同时,四个方向都有了编号,那么在选择路
径的时候也就可以不采用任选的方式了,只 需按照编号依次尝试就可以了。
跳马问题
4 3 2 1
若马所处的位置为 (x,y) , 则其下一步可以到达的四 个位置分别是(x+1, y-2), (x+2, y-1),(x+2, y+1), (x+1, y+2)。
跳马问题
骑士遍历问题的解空间是从左下角到右上角
的所有路径。
解空间的长度是所有路径中最长的路径的长
度。
解空间的组织形式是一棵四叉树,一个可行
的解就是从根节点到叶子节点的一条路径。 控制策略则是马必须在棋盘内。
跳马问题(递归)
procedure search(k: integer); // 递归查找 begin for i := 1 to 4 do // 依次尝试四个方向 if (x + dx[i] <= n) and (y + dy[i] > 0) and (y + dy[i] <= n) then // 在棋盘上 begin route[k] := i; // 记录下当前方向 x := x + dx[i]; y := y + dy[i]; // 修改扩展节点坐标 if (x = n) and (y = m) then // 是否是目标点 begin output(k); halt; // 是目标点,输出结果并终止程序 end else search(k+1); // 不是目标点,继续尝试下一步 // 扩展出的点是死点,回溯 x := x - dx[i]; y := y - dy[i]; // 恢复扩展节点坐标 end; end;
基于Java的骑士游历问题的预见算法
基于Java的骑士游历问题的预见算法学术交流概念RGB模式CMYK模式HSB模式适用对象荧光屏像素发光印刷/喷涂介质反光视觉基本色红、绿、蓝青、紫、黄、黑360种、白、黑颜色数量256×256×256101×101×101×101360×101×101有名合成色黑、白、灰、黄、紫、青红、绿、蓝灰内涵概念明、暗、鲜艳;互补色人的视觉对光的颜色反应。
三种模式中的颜色有同名的,但在各自模式中定义的同名颜色的本质(电磁波长及相混程度)是不同的,不能把一种模式下的颜色当成另一种模式下的同名颜色。
CMYK和RGB模式中没有明确的补色、饱和度、亮度的概念。
补色的概念产生于HSB模式中,意义在于调整它的相对颜色,而应用却是在CMYK模式中和RGB模式中。
数字化处理使用的显示器像素颜色“根深蒂固”地由RGB模式定义,在显示器上观看图像图形在CMYK模式下的效果,本质上是在观看图像图形在RGB模式下的颜色效果(或看到的是图像图形在CMYK模式下的近似效果)———能在RGB模式中对应的CMYK模式颜色表现出CMYK(也是RGB)颜色,不能在RGB模式中对应的CMYK模式颜色,用RGB模式中最近似的颜色代替。
色彩模式相关概念比较:应用色彩模式时要准确对应适当的对象,一般地,人们谈论色彩时使用着HSB模式,而不管色彩(光线)是来自于发光体还是反光体,比如人用明、暗、鲜艳(都是HSB模式中的概念)与否,从感觉上谈论屏幕图像或纸上的印刷品。
色彩模式是出于技术和艺术的需要,为模拟描述和理解颜色而设置的一种方法标准,每种模式有对应的描述对象(发光,反光,视觉),描述都有局限性。
RGB模式用于描述基于屏幕像素发光的光线色彩,CMYK模式用于描述基于颜料、染料的反光色彩,HSB模式用于描述基于人的视觉的光线色彩。
1限于篇幅,这两种模式在另文中详细讨论。
1骑士游历问题在国际象棋的棋盘(8行×8列)上放置一个马,按照“马走日字”的规则,马要遍历棋盘,即到达棋盘上的每一格,并且每格只到达一次。
马的遍历问题c语言
马的遍历问题c语言马的遍历问题是一道经典的算法问题,也是计算机科学中常见的图论问题。
在这个问题中,我们需要找到一种方法来让一个马在一个棋盘上遍历所有的格子,且每个格子只能被访问一次。
这个问题可以使用深度优先搜索或广度优先搜索等算法来解决。
一、问题描述1.1 题目描述在一个8x8的棋盘上,有一个马初始位置为(x0,y0),现在需要将马移动到棋盘上所有格子恰好经过一次且仅经过一次。
请问是否存在这样的移动方案?如果存在,请输出任意一种方案。
1.2 输入输出格式输入格式:两个整数x0和y0表示马初始位置坐标。
输出格式:若存在移动方案,则输出任意一种方案;否则输出“无解”。
二、解题思路2.1 深度优先搜索算法深度优先搜索算法是从根节点开始,沿着树的深度遍历树的节点,直到找到目标节点或者遍历完整棵树。
该算法可以使用递归方式实现,也可以使用栈来实现。
对于本题,在每个节点上,我们需要判断当前位置是否已经被访问过,并且还需要判断当前位置是否越界。
如果当前位置符合要求,则将该位置标记为已访问,并且继续递归搜索下一个节点。
如果搜索到最后一个节点,则说明找到了一种方案。
2.2 广度优先搜索算法广度优先搜索算法是从起始节点开始,按照层次遍历树的节点,直到找到目标节点或者遍历完整棵树。
该算法可以使用队列来实现。
对于本题,在每个节点上,我们需要判断当前位置是否已经被访问过,并且还需要判断当前位置是否越界。
如果当前位置符合要求,则将该位置标记为已访问,并且将该节点加入队列中。
接着从队列中取出下一个节点,继续搜索下一层的所有节点。
如果搜索到最后一个节点,则说明找到了一种方案。
三、代码实现3.1 深度优先搜索算法代码实现```#include <stdio.h>#include <stdlib.h>#define N 8 // 棋盘大小#define MAX_STEP N*N // 最大步数int board[N][N]; // 棋盘int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1}; // 马可以走的8个方向int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};void print_board() // 打印棋盘{int i, j;for(i=0; i<N; i++) {for(j=0; j<N; j++) {printf("%d ", board[i][j]);}printf("\n");}}int dfs(int x, int y, int step) // 深度优先搜索{int k, nx, ny;if(step == MAX_STEP) { // 找到一种方案print_board();return 1;}for(k=0; k<8; k++) { // 枚举马可以走的8个方向nx = x + dx[k];ny = y + dy[k];if(nx>=0 && nx<N && ny>=0 && ny<N && board[nx][ny]==0) { // 判断是否越界和是否已经访问过 board[nx][ny] = step+1;if(dfs(nx, ny, step+1)) { // 递归搜索下一个节点 return 1;}board[nx][ny] = 0;}}return 0;}int main(){int x, y;printf("请输入马初始位置坐标(范围:0~7):\n"); scanf("%d %d", &x, &y);if(x<0 || x>=N || y<0 || y>=N) {printf("输入坐标不合法!\n");return -1;}board[x][y] = 1; // 标记初始位置已访问过if(!dfs(x, y, 1)) { // 搜索所有节点printf("无解!\n");}return 0;}```3.2 广度优先搜索算法代码实现```#include <stdio.h>#include <stdlib.h>#define N 8 // 棋盘大小#define MAX_STEP N*N // 最大步数int board[N][N]; // 棋盘int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1}; // 马可以走的8个方向int dy[8] = {2, 1, -1, -2, -2, -1, 1, 2};void print_board() // 打印棋盘{int i, j;for(i=0; i<N; i++) {for(j=0; j<N; j++) {printf("%d ", board[i][j]);}printf("\n");}}int bfs(int x0, int y0) // 广度优先搜索{int queue[N*N][3];int head = 0;int tail = 0;int i;queue[tail][0] = x0;queue[tail][1] = y0;queue[tail][2] = 1; // 初始位置已访问过tail++;while(head<tail) { // 遍历队列中的所有节点int x = queue[head][0];int y = queue[head][1];int step = queue[head][2];if(step == MAX_STEP) { // 找到一种方案print_board();return 1;}for(i=0; i<8; i++) { // 枚举马可以走的8个方向int nx = x + dx[i];int ny = y + dy[i];if(nx>=0 && nx<N && ny>=0 && ny<N && board[nx][ny]==0) { // 判断是否越界和是否已经访问过 board[nx][ny] = step+1;queue[tail][0] = nx;queue[tail][1] = ny;queue[tail][2] = step+1;tail++;}}head++;}return 0;}int main(){int x, y;printf("请输入马初始位置坐标(范围:0~7):\n"); scanf("%d %d", &x, &y);if(x<0 || x>=N || y<0 || y>=N) {printf("输入坐标不合法!\n");return -1;}board[x][y] = 1; // 标记初始位置已访问过if(!bfs(x, y)) { // 搜索所有节点printf("无解!\n");}return 0;}```四、总结本文介绍了马的遍历问题,包括问题描述、解题思路和代码实现。
跳马问题、骑士遍历问题
// 回朔
int main() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) qipan[i][j]=0; for(i=1;i<=n;i++) //该处分别计算从点(1,1)到点(n,n)作为起始点,可以找到哪些回路 for(int j=1;j<=n;j++) { xLabel= i; yLabel = j; cmq = 1; for(int k1=1;k1<=n;k1++) for(int k2=1;k2<=n;k2++) qipan[k1][k2]=0; qipan[i][j] = 1; tiaoma(i,j); } if(OK!=1) { cout<<endl<<"当n="<<n<<"时无回路"<<endl; } return 0; }
代码
#include<iostream.h> #include <time.h> #include <stdlib.h> const int n=6; // 表示棋盘的长和高n int qipan[n+1][n+1]; // 记录棋盘是否被跳过 static int cmq; // 步数 int OK=0; // 没有被使用 int xLabel,yLabel; void shuchu() { cout<<'\t'; for(int i1=1;i1<=n;i1++) cout<<i1<<"列"<<'\t'; for(int i=1;i<=n;i++) { cout<<endl; cout<<i<<"行"<<'\t'; for(int j=1;j<=n;j++) { cout<<qipan[i][j]<<'\t'; } cout<位置。 在从新的位置选择一个方向前进,继续,直 到无法前进为止。无法前进可能有如下原因: 下一位置超出边界、下一位置已经被访问过。 当马已经无法前进时,就回退到上一位置, 从新选择一个新的方向前进;如果还是无法 前进,就再回退到上一位置,以此类推。
使用回溯算法求解骑士游历问题
求解骑士游历问题显然求解骑士游历问题的每一步就是马在棋盘上走的一步。
在每一步马需要选择一个方向进行游历,这时记住解的每一步需要记住两件事:1.当前步的行列位置2.当前步已经试探过哪些方向了,以便回溯回来时能够选择一个新的方向进行试探所以使用两个数组,数组board记住棋盘的每个位置是在马的第几步到达的,这反映了问题的解,即第几步到哪个位置。
数组direction记住在棋盘的某个位置已经试探过的方向,每个位置有八个方向,可按某种顺序对八个方向编号,然后在每个位置按编号顺序试探方向。
在确定数据结构之后,同样需要确定下面几个问题:1.怎样的状态是初始状态。
2.怎样选择当前步可能的路线3.怎样表示向前推进一步4.怎样回溯及清除当前步的痕迹显然初始状态是棋盘的每个位置都置为第0步到达(即还没有到达),每个位置都还没有选择任何方向(可赋值MAX_DIR(=8)表示没有选择方向)。
选择当前步可能的路线就是在当前位置选择一个方向来游历下一步。
在选择的时候同样需要区分是从第0个方向开始考虑还是从上一次的下一个方向开始考虑。
为了方便从下一个方向开始考虑,实际上数组direction在某一位置(curr_x, curr_y)的值记住的是从上一位置选择了哪个编号的方向而到达的,这样容易回溯到上一位置,而且容易在回溯到上一位置之后从下个一方向重新试探。
向前推进一步则要根据所选择的方向推进到下一位置,记住到下一位置所选择的方向,下一位置是第几步到达的,然后增加步数。
回溯一步则要标记当前位置没有到达过(将到达的步数置为0),根据上一步到当前位置的所选择的方向(这个方向是记录当前位置所对应的direction数组中)而回溯到上一位置,然后减少步数。
下面程序用类KNIGHT来封装数组board、direction、当前位置(curr_x, curr_y)、当前步数(step),并且使用last_direction来记住上一位置到当前位置所选择的方向。
马的遍历问题(C++实现)
马的遍历问题(C++实现)课程设计报告课程名称:题目:院系:专业:班级:学号:姓名:时间:目录一、需求分析 (2)1.问题描述: (2)2.基本要求 (2)二、概要设计 (2)1.数据结构 (2)2.程序模块 (3)三、详细设计 (4)四、调试分析 (9)五、用户手册 (9)六、测试结果 (10)七、参考文献 (13)棋盘求解题目:设计程序完成如下要求:在中国象棋棋盘上,对任一位置上放置的一个马,均能选择一个合适的路线,使得该棋子能按象棋的规则不重复地走过棋盘上的每一位置。
要求依次输出所走过的各位置的坐标。
一、需求分析1. 问题描述:中国象棋是10*9的棋盘,马的走法是“马走日”,这里忽略“蹩脚马”的情况,使马的遍历的问题简单化。
马在棋盘上遍历采用算法当中的深度优先算法和回溯法:回溯法是一种不断试探且及时纠正错误的搜索方法。
从入口出发,按某一个方向向前探索,若能走通并且未走过,即某处可以到达,则到达新点,否则试探下一方向;如果发生“阻塞”的情况,就是后面走不通的情况,则沿原路返回前一点,换下一个方向再继续试探,直到找到一条通路,或无路可走又返回到入口点。
在寻找下一步的时候,对周围的这几个点进行比较,从而分出优劣程度,即看它们周围可以走的点谁最少,然后就走那条可走路线最少的那条。
经过这样的筛选后,就会为后面的路径寻找提供方便,从而减少回溯次数。
棋盘中马的遍历采用数组进行存储,同时因为有回溯,所以采用栈的方法,并且为了最后打印方便,采用的是顺序栈的方法。
同时还有八个方向的数组,和为栈设计的每个点周围的八个方向那些可以走的数组。
2. 基本要求棋盘有10行9列,利用chess来表示一个棋盘,chess[i][j]=0或1;其中:0表示通路,1表示不通,当从某点向下试探时,中间点的8个方向可以试探;棋盘采用数组形式,并且这里的数组比棋盘的数组规模稍大,因为为了保证边缘点也有八个方向可以走,使问题能够简单化,不用再判断当前点的试探方向有几个。
马的遍历课程设计报告
《数据结构》课程设计实验报告——马的遍历学院:长江学院班级:093212学号:09321225姓名:杨海龙指导老师:刘自强指导时间:2010.12.28目录1.流程图: (1)2.问题描述: (2)3.设计思路: (2)4.数据结构设计: (2)5.功能函数算法分析: (3)(1)计算一个点周围有几个点函数int Count(int x,int y) (3)(2)寻找下一个方向函数int Find_Direction(int x,int y) (3)(3)栈的相关函数: (4)(4)骑士遍历函数:void Knight(int x,int y,int v) (5)(5)主函数:void main() (6)(6)棋盘初始化函数void Mark_Che(int v) (7)(7)标记初始化函数void Mark_Dir(int v) (8)6.运行和测试: (8)7.心得体会: (10)马的遍历问题1.流程图:2.问题描述:在中国象棋棋盘上,对任意位置上放置的一个马,均能选择一个合适的路线,使得该棋子(马)能按象棋的规则不重复的走过棋盘上的每一个位置。
要求依次输出所有走过的各位置的坐标。
3.设计思路:首先,中国象棋是10*9的棋盘,马的走法是“马走日”,忽略“蹩脚马”的情况。
其次,这个题目采用的是算法当中的深度优先算法和回溯法:在“走到”一个位置后要寻找下一个位置,如果发生“阻塞”的情况,就是后面走不通的情况,则向后回溯,重新寻找。
在寻找下一步的时候,对周围的这几个点进行比较,从而分出优劣程度,即看它们周围可以走的点谁最少,然后就走那条可走路线最少的那条。
经过这样的筛选后,就会为后面的路径寻找提供方便,从而减少回溯次数。
最后,本程序的棋盘和数组类似,因而采用数组进行存储,同时因为有回溯,所以采用栈的方法,并且为了最后打印方便,采用的是顺序栈的方法。
同时还有八个方向的数组,和为栈设计的每个点周围的八个方向那些可以走的数组。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
if(x+2<=n&&1<=y-1&&qipan[x+2][y-1]==0) { qipan[x+2][y-1]=++cmq; // 5 tiaoma(x+2,y-1); } if(x+1<=n&&1<=y-2&&qipan[x+1][y-2]==0) { qipan[x+1][y-2]=++cmq; // 6 tiaoma(x+1,y-2); } if(1<=x-1&&1<=y-2&&qipan[x-1][y-2]==0) { qipan[x-1][y-2]=++cmq; // 7 tiaoma(x-1,y-2); } if(1<=x-2&&1<=y-1&&qipan[x-2][y-1]==0) { qipan[x-2][y-1]=++cmq; // 8 tiaoma(x-2,y-1); } cmq --; qipan[x][y] = 0; r称骑士遍历问题:在n*n 方格的棋盘上,从任意指定的方格出发, 为象棋中的马寻找一条走遍棋盘每一格并 且只经过一次的一条路径。
问题分析
如下图所示,一只马在棋盘的某一点,它可以朝8 个方向前进,方向向量分别是: (-2,1)、 (-1,2) (1,2)、 (2,1)、(2,-1) 、(1,-2)、 (-1,-2)、(-2,-1)。
从中任选择一个方向前进,到达新的位置。 在从新的位置选择一个方向前进,继续,直 到无法前进为止。无法前进可能有如下原因: 下一位置超出边界、下一位置已经被访问过。 当马已经无法前进时,就回退到上一位置, 从新选择一个新的方向前进;如果还是无法 前进,就再回退到上一位置,以此类推。
经分析,本问题可以运用回溯法的思想求解: 1.该问题的解空间的组织形式是一颗八叉树, 一个可行的解就是从根节点到叶子节点的一 条路径。 2.控制策略则是马必须在棋盘内。
// 回朔
int main() { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) qipan[i][j]=0; for(i=1;i<=n;i++) //该处分别计算从点(1,1)到点(n,n)作为起始点,可以找到哪些回路 for(int j=1;j<=n;j++) { xLabel= i; yLabel = j; cmq = 1; for(int k1=1;k1<=n;k1++) for(int k2=1;k2<=n;k2++) qipan[k1][k2]=0; qipan[i][j] = 1; tiaoma(i,j); } if(OK!=1) { cout<<endl<<"当n="<<n<<"时无回路"<<endl; } return 0; }
int tiaoma(int x,int y) { if(cmq ==n*n && ((x-2==xLabel && y+1 == yLabel) ||(x-1==xLabel && y+2 == yLabel) ||(x+1==xLabel && y+2== yLabel) || (x+2 == xLabel && y+1 == yLabel) ||(x+2 == xLabel && y-1 == yLabel) ||(x+1== xLabel && y-2==yLabel) ||(x-2==xLabel && y-1==yLabel) ||(x-1==xLabel && y-2==yLabel))) { shuchu(); OK=1; return 0; } if(1 <= x-2 && y+1 <= n && qipan[x-2][y+1] == 0) { qipan[x-2][y+1]=++cmq; // 1 tiaoma(x-2,y+1); } if(1<=x-1&&y+2<=n&&qipan[x-1][y+2]==0) { qipan[x-1][y+2]=++cmq; // 2 tiaoma(x-1,y+2); } if(x+1<=n&&y+2<=n&&qipan[x+1][y+2]==0) { qipan[x+1][y+2]=++cmq; // 3 tiaoma(x+1,y+2); } if(x+2<=n&&y+1<=n&&qipan[x+2][y+1]==0) { qipan[x+2][y+1]=++cmq; // 4 tiaoma(x+2,y+1); }
代码
#include<iostream.h> #include <time.h> #include <stdlib.h> const int n=6; // 表示棋盘的长和高n int qipan[n+1][n+1]; // 记录棋盘是否被跳过 static int cmq; // 步数 int OK=0; // 没有被使用 int xLabel,yLabel; void shuchu() { cout<<'\t'; for(int i1=1;i1<=n;i1++) cout<<i1<<"列"<<'\t'; for(int i=1;i<=n;i++) { cout<<endl; cout<<i<<"行"<<'\t'; for(int j=1;j<=n;j++) { cout<<qipan[i][j]<<'\t'; } cout<<endl; } }