三个怪兽和三个和尚过河java版

三个怪兽和三个和尚过河java版

  • 三个怪兽和三个和尚过河java版
    • 简介
    • 代码
      • Appjava
      • ItemStatejava
      • ActionEnumjava
      • BoatDirectionEnum
    • 结果输出

简介

继之前 三个桶分八升水java版 后,《算法的乐趣》第六章-妖怪和和尚过河问题,和之前第五章的算法大同小异,巩固下对于树的深度遍历,以及在遍历的时候对于栈的使用,下面付上java版代码。

代码

App.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();
        }
    }
}

ItemState.java

当前状态的实体类,保存了两岸的和尚和怪物的数量以及下次船的行走方向。
此处不需要记录每一个和尚具体的状态,只需要确定他们的位置和数量即可,用于计算是否会被吃掉

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;
    }
}

ActionEnum.java

枚举类,对于船的行动和常量的对应,主要为了减少主流程内的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;
    }
}

BoatDirectionEnum

枚举类,定义船的方法。

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] ==>

你可能感兴趣的:(算法的乐趣)