回溯算法
- 格式:docx
- 大小:30.47 KB
- 文档页数:10
常用算法四(回溯算法)1、概念回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。
但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
2、基本思想在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。
当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。
(其实回溯法就是对隐式图的深度优先搜索算法)。
若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束。
而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束。
3、用回溯法解题的一般步骤:(1)针对所给问题,确定问题的解空间:首先应明确定义问题的解空间,问题的解空间应至少包含问题的一个(最优)解。
(2)确定结点的扩展搜索规则(3)以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
4、算法应用示例:八皇后问题的递归实现[java]view plaincopy1.public class Empress {2.3.private int n ; //皇后个数4.private int[] x ; //当前解5.private long sum ; //当前已找到的可行方案数6.private static int h ; //记录遍历方案序数7.8.public Empress(){9.this.sum = 0 ; //初始化方案数为1,当回溯到最佳方案的时候,就自增110.this.n = 8 ; //求n皇后问题,由自己定义11.this.x = new int[n+1]; //x[i]表示皇后i放在棋盘的第i行的第x[i]列12. h = 1 ; //这个是我额外定义的变量,用于遍历方案的个数,请看backTrace()中h变量的作用,这里将它定义为static静态变量13. }14.15.public boolean place (int k){16.for (int j = 1 ; j < k ; j++){17.//这个主要是刷选符合皇后条件的解,因为皇后可以攻击与之同一行同一列的或同一斜线上的棋子18.if ( (Math.abs(k - j)) == (Math.abs(x[j]-x[k])) || (x[j] == x[k]) ){19.return false ; //如果是与之同一行同一列的或同一斜线上的棋子,返回false;20. }21. }22.return true ;//如果不是与之同一行同一列的或同一斜线上的棋子,返回true;23. }24.25.public void backTrace (int t){26.if (t > n){ //当t>n时,算法搜索到叶节点,得到一个新的n皇后互不攻击放置方案,方案数加127. sum ++ ; //方案数自增128. System.out.println ("方案" + (h++) + "");29. print(x);30. System.out.print ("\n----------------\n");//华丽的分割线31. }else { //当t<=n时,当前扩展的结点Z是解空间中的内部结点,该节点有x[i]=1,2,…,n共n个子结点,32.//对于当前扩展结点Z的每一个儿子结点,由place()方法检测其可行性,33.//并以深度优先的方式递归地对可行子树搜索,或剪去不可行子数34.for (int i = 1 ; i <= n ; i++){35. x[t] = i ;36.if (place (t)){ //检查结点是否符合条件37. backTrace (t+1); //递归调用38. }39. }40. }41. }42.43.public void print (int[] a){ //打印数组,没啥的44.for (int i = 1 ; i < a.length ; i++){45. System.out.print ("皇后" + i + "在" + i + "行" +a[i] + "列、");46. }47. }48.49.public static void main (String[] args){50. Empress em = new Empress();51. em.backTrace(1); //从1开始回溯52. System.out.println ("\n详细方案如上所示,"+"可行个数为:" + em.sum);53. }54.}/*output:八皇后问题只有92种方案,这里只给出其中的三个方案55.方案156.皇后1在1行1列、皇后2在2行5列、皇后3在3行8列、皇后4在4行6列、皇后5在5行3列、皇后6在6行7列、皇后7在7行2列、皇后8在8行4列、57.----------------58.方案259.皇后1在1行1列、皇后2在2行6列、皇后3在3行8列、皇后4在4行3列、皇后5在5行7列、皇后6在6行4列、皇后7在7行2列、皇后8在8行5列、60.----------------61. .62. .63. .64.方案9265.皇后1在1行8列、皇后2在2行4列、皇后3在3行1列、皇后4在4行3列、皇后5在5行6列、皇后6在6行2列、皇后7在7行7列、皇后8在8行5列、66.----------------67.*///~分书问题:有编号为A、B、C、D、E 的5 本书,以及5 个人,每本书可以分给每一个对该书有兴趣的人阅读,且每个人都只能分到一本自己感兴趣的书。
问当给定5 个人对5 本书的感兴趣情况时,怎样分配这5 本书才能让每个人都开始阅读。
思路:每次都尝试给第p 个人从 5 本书中分出他感兴趣的一本,若不能构成最终解,则撤销回溯到上一个人(即第p – 1 个人)的分配。
我们如下确定:int bookCounts 表示书的总数量,与总人数相等int like [p] [b] = 1 表示第p 个人喜欢读第b 本书,即具体的问题初始条件;int given [b] = p 表示第b 本书分给了第p 个人,即保存解的标识数组;注:在这里p ,b (即下标)都从0 开始,算法实现如下:/*** 回溯法求解分书问题* @author haolloyin*/public class AllacateBooks {// 书的总数量,与总人数相等private int bookCounts = 5;// like[p][b]=1 表示第 p 个人喜欢读第 b 本书private int[][] like = new int[bookCounts][bookCounts];// given[b] = p 表示将第 b 本书分配给第 p 个人private int[] given = new int[bookCounts];// 初始化标识数组 given[] 和传入各人喜欢书的情况数组private void init(int like[][]) {for (int i = 0; i < bookCounts; i++) {given[i] = -1; // -1 表示第 i 本书还没分配出去}this.like = like;}// 尝试给每一个人分配一本书public void allocateBook(int person) {for (int bookNum = 0; bookNum < bookCounts; bookNum++) {if (like[person][bookNum] == 1 && given[bookNum] == -1) {given[bookNum] = person;if (person == bookCounts - 1) {// 打印结果for (int i = 0; i < bookCounts; i++) {System.out.println("人 " + (given[i]+1) + " <---> 书 " + ((char)(i + 'A')));}System.out.println();} else {// 为下一个人分配一本书allocateBook(person + 1);}// 失败,回溯重新寻找解given[bookNum] = -1;}}}// 测试public static void main(String[] args) {// 构造一个问题规模int[][] like = new int[][]{{ 0, 0, 1, 1, 0 },{ 1, 1, 0, 0, 1 },{ 0, 1, 1, 0, 1 },{ 0, 0, 0, 1, 0 },{ 0, 1, 0, 0, 1 }};AllacateBooks allocateBooks = new AllacateBooks();allocateBooks.init(like);allocateBooks.allocateBook(0);}}对应于所给的问题规模,所得的解如下:人 2 <---> 书 A人 3 <---> 书 B人 1 <---> 书 C人 4 <---> 书 D人 5 <---> 书 E人 2 <---> 书 A人 5 <---> 书 B人 1 <---> 书 C人 4 <---> 书 D人 3 <---> 书 E[实验目的]综合运用数组、递归等数据结构知识,掌握、提高分析、设计、实现及测试程序的综合能力。
[实验内容及要求]以一个M×N的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。
设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。