java搜索类问题的通用解

搜索解决方案类问题的通用解法

这类问题有下面的特点:

1、有初始状态,例如3只狼和3只羊需要过河,意味着刚开始3只狼和3只羊都在河的一个岸边。

2、有目标状态,例如3只狼和3只羊要到达河的对岸,意味着最后3只狼和3只羊都要和的对岸。

3、通过一些规则可以使状态发生变化,例如可以用船把2只狼运送到和的对岸,这时候的状态就是河的一边有3只羊和1只狼,河的另一边有2只浪(当然可以根据一边的数字推算出另一边的数字,因为要么在这边要么在那边)。

人是如何来解决这类问题的呢?看下面的图:

《java搜索类问题的通用解》

首先考虑3只狼3只羊在左边,船只能装两只动物,所以有5种方案,然后看看每一种方案产生的结果是不是想要的结果,如果不是,继续看有哪些情况,上面的图展示了这个过程。图中的右表示把动物运送到对岸,左表示船再回来。

程序如何来实现这个过程呢?

程序的实现过程是对人思考问题的模拟。有两种方式,一种方式按照层来找,初始状态下,考虑5种可能,然后对5种可能进行分析,然后对每一种情况的可能变化情况再考虑,直到找到目标状态,或者不能再变化,称为广度优先搜索。另一种方式是先考虑5种情况中的一种,考虑完之后考虑这一种情况能够转移成哪些状态,然后再考虑其中一个,称为深度优先搜索。之前给出的参考答案采用的是深度优先搜索。

例如下图,如果按照广度优先:则每个状态处理的顺序为:1(第一层) 2 3(第二层) 4 5 6(第三层) 7 8 9 10(第四层)

如果按照深度优先:则每个状态的处理顺序为:1 2 4 7 3 5 8 9 6 10,2下面的情况处理完再处理3下面的情况,同样5下面的情况处理完才处理6下面的情况。

《java搜索类问题的通用解》

算法的关键部分如下:

1、需要定义1个结构来表示状态,包括初始状态、中间状态和目标状态。我的参考代码中使用了int state[]表示状态。

2、要明确初始状态和目标状态。代码中初始状态为[3,3]表示岸上有3只狼和3只羊,目标状态为[0,0]表示狼和羊都过河了。

3、要明确状态转换的规则,通常每个规则可以定义为方法,方法的输入是一种状态,而输出是另一种状态。代码中move方法完成了这个功能,只是我把多个规则写在了同一个方法中。

4、编写主程序,从初始状态开始,不断地进行状态转换直到结束。有两种处理方式,方法的递归调用,和循环。

可能存在的问题:

1、死循环,最直观的例子,让一只狼到对岸,然后再回来,然后再过去,然后再回来…,永远不会有结束的时候,对于这个问题需要进行控制,有效的方式是记录走过的状态,如果发现重复,放弃这个方案。程序中有两个地方进行了控制。

2、解的空间可能会非常大,如果不采取措施可能永远也运行不完。例如下象棋,大家都知道谁想的远,谁就可能赢,如果让电脑把100步之后的情况都考虑到,我想人可能很难赢,但是让电脑考虑100步需要太大的计算量,如果考虑每一步有20种走法,则需要考虑20的100次方,可以想象这个数字有多大。对于这个问题,通常会采用启发式搜索以及剪枝等。感兴趣的同学可以参考相关书籍。

——————————————————————————–

三、关键代码分析

完整代码参考:Java编程能力强化——狼羊过河问题

关键点分析:

1、public void next(int state[],StringBuffer str)

递归调用的方法,处理指定的状态下面的所有状态。

2、  if(str==null){ //表示第一步
   // 一只狼一只羊
   newState = move(state,”-1-1″);
   next(newState,new StringBuffer(“-1-1”));
   // 两只狼过河
   newState = move(state,”-2-0″);
   next(newState,new StringBuffer(“-2-0”));
   return;
  }
对初始状态进行处理,也可以合并在下面的代码中,如果合并需要判断str是否为空。另外,这里的return不能省略。

3、  if(state[0]==0 && state[1]==0){ // 全部转移到右岸了
   printResult(str);
   return;
  }
表示得到目标状态,处理结束。

4、   // 两只狼
   if(state[0]>=2 && !str.substring(0,4).equals(“+2+0”)){
    newState = move(state,”-2-0″);
    if(check(newState)){
     next(newState,new StringBuffer(str).insert(0,”-2-0″));
    }
   }
注意:new StringBuffer(str).insert(0,”-2-0″)不能写成str.insert(0,”-2-0″)

另外!str.substring(0,4).equals(“+2+0”)的作用防止去和回船上的动物是一样的。

5、 public int[] move(int state[],String info){
  int lang = 0;
  try{
   lang = Integer.parseInt(info.substring(0,2));
  }catch(Exception e){
   lang = Integer.parseInt(info.substring(1,2));
  }
  int yang = 0;
  try{
   yang= Integer.parseInt(info.substring(2));
  }catch(Exception e){
   yang = Integer.parseInt(info.substring(3));
  }
  int[] result = new int[state.length];
  result[0] = state[0]+lang;
  result[1] = state[1]+yang;
  return result;
 }

注意:try语句的使用,因为+3转换为数字的时候出现异常

另外不能直接修改state的元素值然后把state返回。

6、public boolean check(int state[]){
判定羊的个数不能小于狼的个数。

7、public boolean hasExist(StringBuffer str){
判断是否有死循环。

8、public void printResult(StringBuffer str){
输出结果方案。

大家可以试着使用广度优先的方式实现代码,可以不用递归的方式进行处理(如果递归的层数比较多,会出现堆栈溢出问题)。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/JavaEETeacher/archive/2011/01/28/6167453.aspx

    原文作者:八皇后问题
    原文地址: https://blog.csdn.net/fanhuibin/article/details/51375788
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注