老虎过河

import java.util.HashMap;
import java.util.Iterator;
import java.util.Stack;

/**
 * 编程解决以下问题:
 *   3只大老虎(A、B、C)和3只小老虎(a、b、c)过河,过河的工具是一条小船,
 *   3只大老虎和其中一条小老虎(a)会划船,船一次只能载不管大小2个老虎
 *   过河,但是小老虎必须和母亲在一起,否则就会被其他大老虎吃掉,看看
 *   怎么样过河能保证不被大老虎吃掉安全过河。
 *
 * 实现思路 
 * 1. 初始状态(都在河左岸0000 0000) ----> 终止状态(都在河右岸0111 1111) 
 * 2. 状态表示位表示:0-船, 1-老虎A, 2-老虎a, 3-老虎B, 4-老虎b, 5-老虎C, 6-老虎c, 此位为0表示在左岸,为1表示在右岸; 
 * 3. 构造从初始状态到一下个合法状态有向图. 
 * 4. 构造图一直到没有下一个状态,或者到了终止状态结束停止构造. 
 * 5. 构造图的过程使用广度优先一层一层构造.
 * 6. 递归构造图的过程中如果发现终止结点就返回true,如果返回true就用栈保存当前结点,
 *    从而在递归返回过程中形成一条到终止结点状态转换路径;
 *    递归返回过程得到结果,省去搜索图;
 */
public class TigerAcrossRiver {
    private static final int SHIP = 0;

    private static final int TOTAL_STATUS = 7;

    private static final int BEGIN_STATUS = 0x0; // 初始状态--全都在左岸(0000 0000)

    private static final int END_STATUS = 0x7F; // 终止状态--全都在右岸(0111 1111)

    // 0000 0010 ; A ; 1 << 1
    // 0000 0100 ; a ; 1 << 2
    // 0000 1000 ; B ; 1 << 3
    // 0010 0000 ; C ; 1 << 5
    // 0010 1110 为判断划船者提供方便;
    private static final int SAILOR = 0x2E;

    private static HashMap<Integer, Integer> traceMap = new HashMap<Integer, Integer>();

    private static Stack<Node> step = new Stack<Node>();

    /**
     * 创建一个有向无环图; 创建过程中可以顺便保存路径,省去再搜索图;
     */
    private static boolean createNodeTree(Node curNode) {
        if (curNode == null) {
            return false;
        }

        int status = curNode.status;

        // 已经是终止状态
        if (isSucceed(status)) {
            return true;
        }

        HashMap<Integer, Node> ret = new HashMap<Integer, Node>();
        if (isRight(status, SHIP)) {// 如果船在右岸
            for (int j = 1; j < TOTAL_STATUS; j++) {// 编历6个动物;
                if (isRight(status, j) && isSailor(j)) {// 如果在右岸且会划船的;

                    // 设定划船者,分两种情况,1划船者单独过河,2划船者带一名乘客;
                    int statusTmp = setLeft(setLeft(status, SHIP), j);
                    if (isStatusLegal(statusTmp)) {// 如果单独划船也合法状态也添加到next状态集中;
                        ret.put(statusTmp, new Node(statusTmp, null));
                    }

                    // 带乘客,除划船的只有载一名乘客;
                    int statusTmp2;
                    for (int k = 1; k < TOTAL_STATUS; k++) {// 6只动物
                        if (isRight(statusTmp, k)) {// 在右岸;
                            statusTmp2 = setLeft(statusTmp, k); // 同划船都一起过河
                            if (isStatusLegal(statusTmp2)) {// 合法则加入结果集;
                                ret.put(statusTmp2, new Node(statusTmp2, null));
                            }
                        }
                    }
                }
            }
        } else {// 如果船在左岸
            for (int j = 1; j < TOTAL_STATUS; j++) {// 编历6只动物;
                if (!isRight(status, j) && isSailor(j)) {// 在左岸且会划船
                    int statusTmp = setRight(setRight(status, SHIP), j);
                    if (isStatusLegal(statusTmp)) {
                        ret.put(statusTmp, new Node(statusTmp, null));
                    }

                    // 带乘客,除划船的只有载一名乘客;
                    int statusTmp2;
                    for (int k = 1; k < TOTAL_STATUS; k++) {// 编历6只动物;
                        if (!isRight(statusTmp, k)) {// 在左岸
                            statusTmp2 = setRight(statusTmp, k); // 过河到右岸;
                            if (isStatusLegal(statusTmp2)) {// 合法加入next结果集;
                                ret.put(statusTmp2, new Node(statusTmp2, null));
                            }
                        }
                    }
                }
            }
        }

        if (ret.size() == 0) {
            return false;
        }

        curNode.next = ret;

        for (Iterator it = ret.keySet().iterator(); it.hasNext();) {
            Integer key = (Integer)it.next();
            Node d = ret.get(key);
            if (createNodeTree(d)) {
                step.push(d);
                return true;
            }

        }

        return false;
    }

    // 将sb移到右岸;
    private static int setRight(int status, int sb) {
        return status | (1 << sb);
    }

    // 将sb移到左岸;
    private static int setLeft(int status, int sb) {
        return status & ((~0) ^ (1 << sb));
    }

    // 判断是否终止状态;
    private static boolean isSucceed(int status) {
        return (status == END_STATUS) ? true : false;
    }

    // 判断sb会不会划船的
    private static boolean isSailor(int sb) {
        return (SAILOR & (1 << sb)) == (1 << sb);
    }

    // 判断是否是合法状态;
    private static boolean isStatusLegal(int status) {
        for (int i = 1; i < TOTAL_STATUS; i += 2) {// 编历6只动物
            // 如果父子不在同岸,i是大老虎,i+1就是小老虎;
            if (isRight(status, i) ^ isRight(status, i + 1)) {
                // 那么另外两只大老虎不能和i+1在一起, 否则不安全return false;
                if (
                        (isRight(status, (i + 2) % 6) ^ isRight(status, i + 1)) /* 以6为模找另两只大虎中的一只 */
                        && (isRight(status, (i + 4) % 6) ^ isRight(status, i + 1)) /* 另外两只大虎中第二只 */
                    ) {
                    continue;
                }
                else
                {   
                    //不安全return false;
                    return false;
                }
            }
        }

        if (traceMap.containsKey(status)) {
            // 已经编历过的结点不用再处理
            // 这里return false是为了不加入next子结点集,确保有向图无环;
            return false;
        } else {
            traceMap.put(status, status);
        }

        return true;
    }

    // 检查当前状态下,sb是否在右岸;
    private static boolean isRight(int status, int sb) {
        return (status & (1 << sb)) == (1 << sb);
    }

    static class Node {
        public Node(int s, HashMap<Integer, Node> n) {
            this.status = s;
            this.next = n;
        }

        public int status;

        public HashMap<Integer, Node> next;
    }

    // 打印当前状态
    private static void printStatus(int status) {
        StringBuffer strTmp = new StringBuffer("AaBbCc|>  <|AaBbCc");
        if (isRight(status, SHIP)) {
            strTmp.replace(7, 8, " ");
        } else {
            strTmp.replace(10, 11, " ");
        }
        for (int i = 1; i < TOTAL_STATUS; i++) {
            if (isRight(status, i)) {
                strTmp.replace(i - 1, i, " ");
            } else {
                strTmp.replace(i + 11, i + 12, " ");
            }
        }
        System.out.println(strTmp);
    }

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        System.out.println("Start time : " + startTime);
        
        Node root = new Node(BEGIN_STATUS, null);
        traceMap.put(BEGIN_STATUS, BEGIN_STATUS);
        createNodeTree(root);
        step.push(root);

        for (int i = step.size() - 1; i >= 0; i--) {
            printStatus(step.get(i).status);
        }
        
        long endTime = System.currentTimeMillis();
        System.out.print("End time : " + endTime + ", cost " 
                + (endTime - startTime) + "ms");
    }
}

 

 

 
Start time : 1364219778187
AaBbCc|>   |     
A BbC |   <| a   c
AaBbC |>   |     c
A B C |   <| a b c
AaB C |>   |   b c
Aa    |   <|  BbCc
AaBb  |>   |    Cc
  Bb  |   <|Aa  Cc
  BbCc|>   |Aa   
   b c|   <|AaB C
 a b c|>   |A B C
   b  |   <|AaB Cc
  Bb  |>   |Aa  Cc
      |   <|AaBbCc
End time : 1364219778187, cost 0ms

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值