状态树搜索算法再现————妖怪与和尚过河问题

今天继续看了状态树搜索算法,问题是三个和尚三个妖怪过河,无论是过河前还是过河后,和尚的数目都不能小于妖怪的数目,不然就会被吃掉。

题目的初始条件是三个和尚和妖怪都在河的一边,然后每次只能够坐上两个上船,然后到达另一边,其中一个再坐船回去。

其实这个就跟我前几天做的那个状态树搜索算法,三个水桶平分水问题一样,都是搜索状态树,解决这个问题需要找到一条从初始状态变换到终止状态的路径,使用穷举法,遍历所有由妖怪、和尚和小船位置构成的空间状态,寻找从初始状态到最终状态的变换路径。

建立数学模型,描述问题的数据结构,问题的状态模型不仅要描述静止状态,也要描述转换动作,因为这对算法设计至关重要。

状态的数学模型与状态树,三个和尚与三个妖怪和一个小船,7个属性,但是有效的空间状态其实只有5个,过河前的和尚妖怪数目,小船位置,过河后的妖怪和尚数目。

所以我们只要记录这五个属性就可以了,我用的是字符串备忘录,前面4个字符记录过河前后的和尚妖怪数目,最后几个英文是小船的状态,然后比较状态就是字符串的匹配问题了。

作者用C++做,他用到了结构体和枚举变量,

在java中有个枚举类,所以我是用枚举类来实现的。

搜索算法,状态树的遍历,状态树的遍历里面暗含了状态树生产的过程,促使上一个状态向下一个状态转换驱动。

动作模型里面定义了10个动作,10个动作是这个问题游戏中所有可能出现的过河动作,但是并不是所有的动作状态都符合问题要求,所以必须加上判断条件。


剪枝和重复状态判断:

判断一个状态是否适用于当前状态,要有判断合法的算法。

还要有判断是否重复,避免出现死循环,否则,算法可能无法跳出当前状态,java中会出现栈溢出,所以要实现剪枝操作,让深度遍历可以快速收敛。

最后结果是由4种过河方案。

我的java代码如下:

主类用于启动算法实现类

package 状态树搜索算法再现妖怪和尚过河;

public class Main {
	public static void main(String[] args) {
		Main ma = new Main();
		ma.initMain();
	}
	public void initMain(){
		GoToRiver gv = new GoToRiver();
		int[] str = {3,3,0,0};
		StateNode root = new StateNode(str);
		gv.SearchState(root);
	}
}

状态节点类,用于存放空间状态,分析判断空间状态是否合法

package 状态树搜索算法再现妖怪和尚过河;

public class StateNode {
	public int[] NumStr = new int[4]; //一维数组存放当前和尚妖怪的数量,0和2表示和尚,1和3表示妖怪
	public String boatState = "LOCAL";//用字符串表示当前船的位置    	河对岸是REMOTE
	public StateNode back ;
	public StateNode(int[] str){
		NumStr[0] = str[0];
		NumStr[1] = str[1];
		NumStr[2] = str[2];
		NumStr[3] = str[3];
	}
	public boolean IsFinalState(StateNode current){//查看节点是否是最终状态
		if(  current.getText(current).equals("0033REMOTE") ){//如果是最终状态
			return true;
		}
		return false;
	}
	/**
	 * 判断过河是否合法
	 * 
	 * @return
	 */
	public boolean CanTakeAction(StateNode node,AcrossRiverMethod as){//和尚要大于妖怪
		if( node.boatState == as.boatstate ){//当前的船和移动后的船在同一边就返还false
			return false;
		}
		if( node.NumStr[1] + as.monsterAction < 0 ||
				node.NumStr[1] +as.monsterAction > 3 ){//移动后的妖怪数目小于0大于3的话,返回false
			return false;
		}
		if( node.NumStr[0] + as.monkAction < 0 ||
				node.NumStr[0] + as.monkAction > 3 ){//移动后的和尚数目小于0大于3的话,返回false
			return false;
		}
		return true;
	}
	public String getText(StateNode node){//备忘录
		String s = Integer.toString(node.NumStr[0]) + Integer.toString(node.NumStr[1])
		+Integer.toString(node.NumStr[2]) + Integer.toString(node.NumStr[3]) + node.boatState;
		return s;
	}
	public boolean MakeActionNewState(StateNode current,StateNode next,
			AcrossRiverMethod acrossRiverMethod){//传入一个过河的状态
		if( current.CanTakeAction(current,acrossRiverMethod) ){//判断是否能够过河,能够过河就执行过河操作
			next.NumStr[0] = current.NumStr[0] + acrossRiverMethod.monkAction;//下一个状态为当前状态加上操作后的状态
			next.NumStr[2] = 3 - next.NumStr[0];
			next.NumStr[1] = current.NumStr[1] + acrossRiverMethod.monsterAction;
			next.NumStr[3] = 3 - next.NumStr[1];
			next.boatState = acrossRiverMethod.boatstate;
			return true;
		}
		return false;
	}
	public boolean IsValidState(StateNode node){
		if( node.NumStr[0] > 0 && node.NumStr[1] > node.NumStr[0] ){
			return false;
		}
		if( node.NumStr[2] > 0 && node.NumStr[3] > node.NumStr[2] ){
			return false;
		}
		return true;
	}
}

算法实现类,用递归算法实现状态树的遍历:

package 状态树搜索算法再现妖怪和尚过河;

public class GoToRiver {
	public void SearchState(StateNode node){
		if( node.IsFinalState(node) ){
			Tool.PrintResult(node);
			System.out.println("succeed to across the river!");
			return ;
		}
		for(int i=0;i<AcrossRiverMethod.values().length;i++){
			SearchStateOnNewAction(node,AcrossRiverMethod.values()[i]);
		}
	}
	
	public void SearchStateOnNewAction(StateNode node,AcrossRiverMethod acrossRiverMethod){
		StateNode next = new StateNode(node.NumStr);
		boolean DnewState = next.MakeActionNewState(node, next, acrossRiverMethod);
		if( DnewState ){
			next.back = node;
			if( next.IsValidState(next) && !Tool.ISProcessedState(next) ){
				SearchState(next);
			}
		}
	}
}
工具类,用于查找备忘录帮助分析判断状态是否合法。

在有符合状态的时候,输出状态过程

package 状态树搜索算法再现妖怪和尚过河;

import java.util.Vector;

public class Tool {
	/**
	 * 如果state已经存在,返回true
	 * @param state
	 * @return
	 */
	public static boolean ISProcessedState(StateNode state){
		StateNode current = state;//current为当前状态的对象
		while( state.back != null ){//back为上一对象
			if( current.getText(current).equals(state.back.getText(state.back)) ){//如果back存在就判断当前状态是否存在,存在返回true
				return true;
			}
			state = state.back;//逐层查找
		}
		return false;
	}
	public static void PrintResult(StateNode node){
		System.out.println(node.getText(node));
		while( node.back != null ){
			System.out.println(node.back.getText(node.back));
			node = node.back;
		}
	}
}

最后一个是枚举类,存放10种过河状态

package 状态树搜索算法再现妖怪和尚过河;

public enum AcrossRiverMethod{
	ONE_MONSTER_GO("REMOTE",-1,0),// -1代表妖怪改变的数量 0代表和尚改变的数量
	TWO_MONSTER_GO("REMOTE",-2,0),// 代表左边岸上的变动情况
	ONE_MONK_GO("REMOTE",0,-1),
	TWO_MONK_GO("REMOTE",0,-2)
	,ONE_MONSTER_ONE_MONK_GO("REMOTE",-1,-1),
	ONE_MONSTER_BAKC("LOCAL",1,0),
	TWO_MONSTER_BACK("LOCAL",2,0),
	ONE_MONK_BACK("LOCAL",0,1),
	TWO_MONK_BACK("LOCAL",0,2),
	ONE_MONSTER_ONE_MONK_BACK("LOCAL",1,1);
	
	public String boatstate;
	public int monsterAction,monkAction;
	
	AcrossRiverMethod(String boatstate,int monsterAction,int monkAction){
		this.boatstate = boatstate;
		this.monsterAction = monsterAction;
		this.monkAction = monkAction;
	}
}
下面是我的测试图,答案却是是4种没错:
状态树搜索算法再现————妖怪与和尚过河问题_第1张图片



状态树搜索算法再现————妖怪与和尚过河问题_第2张图片


作者博客:http://blog.csdn.net/orbit/article/details/6630461

你可能感兴趣的:(java,算法,搜索)