继之前 三个桶分八升水java版 后,《算法的乐趣》第六章-妖怪和和尚过河问题,和之前第五章的算法大同小异,巩固下对于树的深度遍历,以及在遍历的时候对于栈的使用,下面付上java版代码。
主类,包含了所有算法和流程
package com.yixin.arithmetic.three;
import com.yixin.arithmetic.three.enums.ActionEnum;
import com.yixin.arithmetic.three.enums.BoatDirectionEnum;
import com.yixin.arithmetic.three.model.ItemState;
import java.util.Iterator;
import java.util.Stack;
/**
* 有三个和尚和三个妖怪要利用一个小船过河
* 一个船同时最多只能做两个人,同时,无论在两岸还是再船上
* 当妖怪的数量大于和尚的数量时,和尚就会被吃掉
* 现在需要安排一种过河的方式,保证和尚和妖怪都能顺利过河,并且和尚不会被吃掉。
*/
public class App {
static int index = 0;
private static Stack itemStateStack = new Stack();
public static void main( String[] args ) {
ItemState itemState = new ItemState();
itemState.setLocalMonk(3);
itemState.setLocalMonster(3);
itemState.setBoatDirection(BoatDirectionEnum.LOCAL_2_REMOTE);
itemStateStack.add(itemState);
search();
}
/**
* 核心方法,查询,遍历树
*/
public static void search(){
ItemState itemState = itemStateStack.peek();
ActionEnum[] actionEnumAry = ActionEnum.values();
logIfEnd(itemState);
for(ActionEnum actionEnum : actionEnumAry){
if(!canAcross(itemState , actionEnum)){
continue;
}
ItemState nextItemState = across(itemState , actionEnum);
if (isAlreadyProcess(nextItemState)) {
continue;
}
//利用栈的数据结构,遍历树形结构
itemStateStack.add(nextItemState);
search();
itemStateStack.pop();
}
}
/**
* 查询当前状态是否可以过河
* @param itemState 当前状态
* @param actionEnum 当前尝试的动作
* @return 是否可以
*/
private static boolean canAcross(ItemState itemState , ActionEnum actionEnum){
//移动后在local的和尚数量小于怪兽数量不行
//移动后在remote的和尚数量小于怪兽数量不行
//移动后local或remote的和尚和怪兽数量小于0不行
if(itemState.getBoatDirection() == actionEnum.nextBoatDirection){
return false;
}
if(itemState.getLocalMonk() + actionEnum.monkCount < 0
|| itemState.getLocalMonster() + actionEnum.monsterCount < 0
|| itemState.getRemoteMonk() - actionEnum.monkCount < 0
|| itemState.getRemoteMonster() - actionEnum.monsterCount < 0){
return false;
}
if(itemState.getLocalMonk() + actionEnum.monkCount > 0 && itemState.getLocalMonk() + actionEnum.monkCount < itemState.getLocalMonster() + actionEnum.monsterCount){
return false;
}
if(itemState.getRemoteMonk() - actionEnum.monkCount >0 && itemState.getRemoteMonk() - actionEnum.monkCount < itemState.getRemoteMonster() - actionEnum.monsterCount){
return false;
}
return true;
}
/**
* 真正过河的动作,会进行对象的复制和修改
* @param itemState 当前的状态
* @param actionEnum 当前变化的动作
* @return 复制后的对象,不会有引用地址相同的问题
*/
private static ItemState across(ItemState itemState , ActionEnum actionEnum){
ItemState copyItemState = itemState.clone();
copyItemState.setLocalMonk(itemState.getLocalMonk() + actionEnum.monkCount);
copyItemState.setLocalMonster(itemState.getLocalMonster() + actionEnum.monsterCount);
copyItemState.setRemoteMonk(itemState.getRemoteMonk() - actionEnum.monkCount);
copyItemState.setRemoteMonster(itemState.getRemoteMonster() - actionEnum.monsterCount);
copyItemState.setBoatDirection(actionEnum.nextBoatDirection);
copyItemState.setActionEnum(actionEnum);
return copyItemState;
}
/**
* 因为针对树形结构遍历,需要判断是否已经遍历过当前的节点,若遍历过,则结束
* @param itemState 当前的节点状态
* @return 是否被遍历过
*/
private static boolean isAlreadyProcess(ItemState itemState){
Iterator itemStateIterator = itemStateStack.iterator();
while(itemStateIterator.hasNext()){
ItemState item = itemStateIterator.next();
if(item.equals(itemState)){
return true;
}
}
return false;
}
/**
* 如果结束打印日志
* 通过子节点,依次找到对应的父节点,打印出来
* @param itemState 结束的子节点
*/
private static void logIfEnd(ItemState itemState){
if(itemState.getLocalMonk() == 0 && itemState.getLocalMonster() == 0){
index++;
Iterator iterator = itemStateStack.iterator();
System.out.print(index+":");
while(iterator.hasNext()) {
ItemState item = iterator.next();
System.out.print((item.getActionEnum() == null ? "" : item.getActionEnum().name()) + "[" + item.getLocalMonk() + "," + item.getLocalMonster() + "<->" + item.getRemoteMonk() + "," + item.getRemoteMonster() + "] ==> " );
}
System.out.println();
}
}
}
当前状态的实体类,保存了两岸的和尚和怪物的数量以及下次船的行走方向。
此处不需要记录每一个和尚具体的状态,只需要确定他们的位置和数量即可,用于计算是否会被吃掉
package com.yixin.arithmetic.three.model;
import com.yixin.arithmetic.three.enums.ActionEnum;
import com.yixin.arithmetic.three.enums.BoatDirectionEnum;
/**
* Created by yixin on 16/12/12.
*/
public class ItemState implements Cloneable{
private int localMonster;
private int remoteMonster;
private int localMonk;
private int remoteMonk;
private BoatDirectionEnum boatDirection;
private ActionEnum actionEnum;
public int getLocalMonster() {
return localMonster;
}
public void setLocalMonster(int localMonster) {
this.localMonster = localMonster;
}
public int getRemoteMonster() {
return remoteMonster;
}
public void setRemoteMonster(int remoteMonster) {
this.remoteMonster = remoteMonster;
}
public int getLocalMonk() {
return localMonk;
}
public void setLocalMonk(int localMonk) {
this.localMonk = localMonk;
}
public int getRemoteMonk() {
return remoteMonk;
}
public void setRemoteMonk(int remoteMonk) {
this.remoteMonk = remoteMonk;
}
public BoatDirectionEnum getBoatDirection() {
return boatDirection;
}
public void setBoatDirection(BoatDirectionEnum boatDirection) {
this.boatDirection = boatDirection;
}
public ActionEnum getActionEnum() {
return actionEnum;
}
public void setActionEnum(ActionEnum actionEnum) {
this.actionEnum = actionEnum;
}
public ItemState clone(){
try {
return (ItemState)super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ItemState itemState = (ItemState) o;
if (localMonster != itemState.localMonster) return false;
if (remoteMonster != itemState.remoteMonster) return false;
if (localMonk != itemState.localMonk) return false;
if (remoteMonk != itemState.remoteMonk) return false;
return boatDirection == itemState.boatDirection;
}
@Override
public int hashCode() {
int result = localMonster;
result = 31 * result + remoteMonster;
result = 31 * result + localMonk;
result = 31 * result + remoteMonk;
result = 31 * result + (boatDirection != null ? boatDirection.hashCode() : 0);
return result;
}
}
枚举类,对于船的行动和常量的对应,主要为了减少主流程内的if else的代码。
package com.yixin.arithmetic.three.enums;
/**
* Created by yixin on 16/12/12.
*/
public enum ActionEnum {
TWO_MONSTER_GO(0 , -2 , BoatDirectionEnum.REMOTE_2_LOCAL),
ONE_MONSTER_GO(0 , -1 , BoatDirectionEnum.REMOTE_2_LOCAL),
ONE_MONK_GO(-1 , 0 , BoatDirectionEnum.REMOTE_2_LOCAL),
TWO_MONK_GO(-2 , 0 , BoatDirectionEnum.REMOTE_2_LOCAL),
ONE_MONSTER_ONE_MONK_GO(-1 , -1 , BoatDirectionEnum.REMOTE_2_LOCAL),
TWO_MONSTER_BACK( 0 , 2 , BoatDirectionEnum.LOCAL_2_REMOTE),
ONE_MONSTER_BACK(0 , 1 , BoatDirectionEnum.LOCAL_2_REMOTE),
ONE_MONK_BACK(1 , 0 , BoatDirectionEnum.LOCAL_2_REMOTE),
TWO_MONK_BACK(2 , 0 , BoatDirectionEnum.LOCAL_2_REMOTE),
ONE_MONSTER_ONE_MONK_BACK( 1 , 1 , BoatDirectionEnum.LOCAL_2_REMOTE);
public int monkCount;
public int monsterCount;
public BoatDirectionEnum nextBoatDirection;
ActionEnum(int monkCount, int monsterCount, BoatDirectionEnum boatDirection) {
this.monkCount = monkCount;
this.monsterCount = monsterCount;
this.nextBoatDirection = boatDirection;
}
}
枚举类,定义船的方法。
package com.yixin.arithmetic.three.enums;
/**
* Created by yixin on 16/12/12.
*/
public enum BoatDirectionEnum {
LOCAL_2_REMOTE , REMOTE_2_LOCAL;
}
1:[3,3<->0,0] ==> TWO_MONSTER_GO[3,1<->0,2] ==> ONE_MONSTER_BACK[3,2<->0,1] ==> TWO_MONSTER_GO[3,0<->0,3] ==> ONE_MONSTER_BACK[3,1<->0,2] ==> TWO_MONK_GO[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_BACK[2,2<->1,1] ==> TWO_MONK_GO[0,2<->3,1] ==> ONE_MONSTER_BACK[0,3<->3,0] ==> TWO_MONSTER_GO[0,1<->3,2] ==> ONE_MONSTER_BACK[0,2<->3,1] ==> TWO_MONSTER_GO[0,0<->3,3] ==>
2:[3,3<->0,0] ==> TWO_MONSTER_GO[3,1<->0,2] ==> ONE_MONSTER_BACK[3,2<->0,1] ==> TWO_MONSTER_GO[3,0<->0,3] ==> ONE_MONSTER_BACK[3,1<->0,2] ==> TWO_MONK_GO[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_BACK[2,2<->1,1] ==> TWO_MONK_GO[0,2<->3,1] ==> ONE_MONSTER_BACK[0,3<->3,0] ==> TWO_MONSTER_GO[0,1<->3,2] ==> ONE_MONK_BACK[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_GO[0,0<->3,3] ==>
3:[3,3<->0,0] ==> ONE_MONSTER_ONE_MONK_GO[2,2<->1,1] ==> ONE_MONK_BACK[3,2<->0,1] ==> TWO_MONSTER_GO[3,0<->0,3] ==> ONE_MONSTER_BACK[3,1<->0,2] ==> TWO_MONK_GO[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_BACK[2,2<->1,1] ==> TWO_MONK_GO[0,2<->3,1] ==> ONE_MONSTER_BACK[0,3<->3,0] ==> TWO_MONSTER_GO[0,1<->3,2] ==> ONE_MONSTER_BACK[0,2<->3,1] ==> TWO_MONSTER_GO[0,0<->3,3] ==>
4:[3,3<->0,0] ==> ONE_MONSTER_ONE_MONK_GO[2,2<->1,1] ==> ONE_MONK_BACK[3,2<->0,1] ==> TWO_MONSTER_GO[3,0<->0,3] ==> ONE_MONSTER_BACK[3,1<->0,2] ==> TWO_MONK_GO[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_BACK[2,2<->1,1] ==> TWO_MONK_GO[0,2<->3,1] ==> ONE_MONSTER_BACK[0,3<->3,0] ==> TWO_MONSTER_GO[0,1<->3,2] ==> ONE_MONK_BACK[1,1<->2,2] ==> ONE_MONSTER_ONE_MONK_GO[0,0<->3,3] ==>