zoukankan      html  css  js  c++  java
  • 农夫过河问题

    问题描述:

        农夫要把狼、白菜、羊带到河的对岸,但每次最多只能带一个,当农夫不在时,狼会吃羊,羊会吃白菜,因此狼和羊在同一岸时农夫必须也在,羊和白菜在同一岸时农夫也必须在。问:农夫应该怎样把他们安全带过去?

    我们可以想到这样一个解决方案:

    1、都在左岸
    2、农夫把羊带到右岸
    3、农夫独自回到左岸
    4、农夫把白菜带到右岸
    5、农夫把羊带回左岸
    6、农夫把狼带到右岸
    7、农夫独自回到左岸
    8、农夫把羊带到右岸
    9、都到了右岸

    1、Java实现代码:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Queue;
    
    /**
     * 农夫过河问题
     * <p>
     * JDK: 1.6
     * 
     * @author wuqianling
     * 
     */
    public class FarmerProblem {
    
        /**
         * 判断农夫的位置(1000) <br>
         * 0表示在左岸,1表示在右岸
         * 
         * @param location
         *            当前位置状态
         * @return false 左岸,true 右岸
         */
        public static boolean farmerLocation(int location) {
            return (0 != (location & 0x08)); // &是按位与,0x08表示十六进制数8(二进制为00001000)
        }
    
        /**
         * 判断狼的位置(0100) <br>
         * 0表示在左岸,1表示在右岸
         * 
         * @param location
         *            当前位置状态
         * @return false 左岸,true 右岸
         */
        public static boolean wolfLocation(int location) {
            return (0 != (location & 0x04)); // 0x04表示十六进制数4(二进制为00000100)
        }
    
        /**
         * 判断白菜的位置(0010) <br>
         * 0表示在左岸,1表示在右岸
         * 
         * @param location
         *            当前位置状态
         * @return false 左岸,true 右岸
         */
        public static boolean cabbagaLocation(int location) {
            return (0 != (location & 0x02)); // 0x02表示十六进制数2(二进制为00000010)
        }
    
        /**
         * 判断羊的位置(0001) <br>
         * 0表示在左岸,1表示在右岸
         * 
         * @param location
         *            当前位置状态
         * @return false 左岸,true 右岸
         */
        public static boolean goatLocation(int location) {
            return (0 != (location & 0x01)); // 0x01表示十六进制数1(二进制为00000001)
        }
    
        /**
         * 判断安全状态
         * <p>
         * 1、羊与白菜在同岸,且羊与农夫不在同岸,羊会吃白菜,不安全<br>
         * 2、羊与狼在同岸,且羊与农夫不在同岸,狼会吃羊,不安全<br>
         * 3、其他状态安全
         * 
         * @param location
         *            当前位置状态
         * @return 返回true为安全,false为不安全
         */
        public boolean isSafe(int location) {
            // 羊与白菜在同岸,且羊与农夫不在同岸,羊会吃白菜,不安全
            if ((goatLocation(location) == cabbagaLocation(location))
                    && (goatLocation(location) != farmerLocation(location)))
                return false;
            // 羊与狼在同岸,且羊与农夫不在同岸,狼会吃羊,不安全
            if ((goatLocation(location) == wolfLocation(location))
                    && (goatLocation(location) != farmerLocation(location)))
                return false;
    
            // 其他状态均安全
            return true;
        }
    
        /**
         * 农夫过河问题
         * <p>
         * 问题描述:农夫要把狼、白菜、羊带到对岸,但每次最多只能带一个,当农夫不在时,狼会吃羊,羊会吃白菜。
         * 
         * @return
         */
        public List<Integer> farmerProblem() {
    
            Queue<Integer> moveTO = new LinkedList<Integer>(); // 创建空队列,用于记录可以安全到达的中间状态
            moveTO.add(new Integer(0));// 初始状态进队列
    
            int[] route = new int[16]; // 用于记录已考虑的状态路径
            // 初始化数组route
            for (int i = 0; i < 16; i++) {
                route[i] = -1;
            }
            route[0] = 0;
    
            while (!moveTO.isEmpty() && (route[15] == -1)) {
                int location = moveTO.poll(); // 取队顶状态为当前状态,并移除队顶元素
    
                for (int movers = 1; movers <= 8; movers <<= 1) { // 考虑各种物品移动。
                    if ((0 != (location & 0x8)) == (0 != (location & movers))) { // 农夫与移动的物品在同一岸
                        int newlocation = location ^ (0x08 | movers); // 计算新状态,^为按位异或,相同为0,不同为1
                        if (isSafe(newlocation) && (route[newlocation] == -1)) { // 新状态安全且未处理
                            route[newlocation] = location; // 记录新状态的前驱
                            moveTO.add(new Integer(newlocation)); // 新状态入队
                        }
                    }
                }
            }
            // 保存状态
            List<Integer> resultList = new ArrayList<Integer>();
            if (route[15] != -1) { // 到达最终状态
                for (int location = 15; location >= 0; location = route[location]) {
                    resultList.add(location);
                    if (location == 0) {
                        break;
                    }
                }
            }
            Collections.reverse(resultList); // 反转顺序
            return resultList;
        }
    
        /** 记录上次位置 */
        private int oldLoca = 0;
    
        /**
         * 中文解释
         * 
         * @param location
         */
        private String explainToChinese(int location) {
            String str = "";
    
            if (farmerLocation(location) != farmerLocation(oldLoca))
                str += "农夫"; // 判断农夫的位置是否改变
    
            if (wolfLocation(location) != wolfLocation(oldLoca))
                str += "把狼带到了"; // 判断狼的位置是否改变
            else if (cabbagaLocation(location) != cabbagaLocation(oldLoca))
                str += "把白菜带到了"; // 判断白菜的位置是否改变
            else if (goatLocation(location) != goatLocation(oldLoca))
                str += "把羊带到了"; // 判断羊的位置是否改变
            else
                str += "独自回到了"; // 狼、白菜、羊的位置都未改变
    
            if (farmerLocation(location))
                str += "右岸"; // 判断下一步农夫是在哪一岸,若农夫当前在左岸,则他下一步必在右岸
            else
                str += "左岸";
    
            oldLoca = location; // 记录本次位置,为下次作准备
    
            return str;
        }
    
        /**
         * 获得整型数转换为四位二进制的字符串<br>
         * 例如:9 = 1001 、 2 = 0010
         * 
         * @param n
         *            0到15的十进制整数
         * @return 一个四位的二进制字符串
         */
        private String getIntToFourBinaryStr(int n) {
            char[] bnum = Integer.toBinaryString(n).toCharArray(); // 转为二进制,但是缺少前导0
            char[] bit = { '0', '0', '0', '0' };
            for (int i = bit.length - 1, j = bnum.length - 1; i >= 0 && j >= 0; i--, j--) {
                bit[i] = bnum[j];
            }
            return new String(bit);
        }
    
        /**
         * 输出结果
         * 
         * @param list
         *            一个解集合
         */
        private void outputResult(List<Integer> list) {
            System.out.println("0代表在左岸,1代表在右岸");
            System.out.println("0000 每位数分别代表农夫、狼、白菜、羊");
            System.out.println();
    
            System.out.println("[位置]	[二进制]	[中文解释]");
            for (int i = 0; i < list.size(); i++) {
                int location = list.get(i);
                String str = "位置:" + location + "	";
                str += getIntToFourBinaryStr(location); // 为了利于观察,转换为二进制输出
    
                if (location == 0) {
                    str += "	都在左岸";
                } else {
                    str += "	" + explainToChinese(location); // 中文解释
                }
                System.out.println(str);
                if (location == 15) {
                    System.out.println("		都在右岸");
                }
            }
        }
    
        /**
         * 主方法
         */
        public static void main(String[] args) {
            FarmerProblem fp = new FarmerProblem();
            List<Integer> list = fp.farmerProblem();
            fp.outputResult(list);
        }
    
    }

    运行结果:

    2、为了更直观的模拟过程,我们使用图形界面动画来实现,下面是图形界面的代码:

    import java.awt.BorderLayout;
    import java.awt.Color;
    import java.awt.Graphics;
    import java.awt.TextField;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.util.List;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JPanel;
    import javax.swing.JTextField;
    
    /**
     * 窗口类
     * 
     * @author wuqianling
     * 
     */
    public class MyWindow extends JFrame {
    
        private List<Integer> list = null;
        private DrawPanel drawPanel;
        private TextField textField;
    
        public MyWindow() {
            this.setTitle("农夫过河");
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 设置窗口关闭时退出程序
            this.setSize(460, 320);
            this.setResizable(false); // 固定窗口大小
            this.setLocationRelativeTo(null); // 居中
            // 顶部面板容器
            JPanel top = new JPanel();
            top.setLayout(new BorderLayout()); // 设置边界布局管理器
    
            textField = new TextField("0000 每位数分别代表农夫、狼、白菜、羊");
            textField.setEditable(false); // 设置不可编辑
            top.add(textField, BorderLayout.CENTER);
    
            JPanel topRight = new JPanel();
            JButton calbtn = new JButton("计算方案");
            JButton simbtn = new JButton("模拟方案");
            topRight.add(calbtn);
            topRight.add(simbtn);
            top.add(topRight, BorderLayout.EAST);
    
            this.getContentPane().add(top, BorderLayout.NORTH); // 添加到窗口中
    
            // 添加模拟动画面板
            drawPanel = new DrawPanel();
            this.getContentPane().add(drawPanel);
    
            // 添加按钮事件监听
            calbtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    calculate();
                }
            });
    
            simbtn.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    if (list == null)
                        calculate();
                    drawPanel.startDraw();
                }
            });
        }
    
        /**
         * 计算方案
         */
        private void calculate() {
            FarmerProblem nf = new FarmerProblem();
            list = nf.farmerProblem();
    
            String str = "一个解决方案:";
            int i = 0;
            for (; i < list.size() - 1; i++) {
                str += (list.get(i) + ",");
            }
            str += list.get(i);
    
            textField.setText(str);
            System.out.println("计算得" + str);
        }
    
        /**
         * 主方法
         * 
         * @param args
         */
        public static void main(String[] args) {
            MyWindow win = new MyWindow();
            win.setVisible(true);
        }
    
        /**
         * 绘制模拟的动画,实现Runnable接口开启线程
         * 
         * @author wuqianling
         * 
         */
        final class DrawPanel extends JPanel implements Runnable {
    
            private boolean isRun = false; // 是否正在进行模拟
    
            private final static int INITVALUE = 4;// 默认初始坐标值
            private int nx = INITVALUE; // 农夫的x轴坐标
            private int lx = INITVALUE; // 狼的x轴坐标
            private int bx = INITVALUE; // 白菜的x轴坐标
            private int yx = INITVALUE; // 羊的x轴坐标
    
            @Override
            public void paint(Graphics g) {
                // g.clearRect(0, 0, getWidth(), getHeight());
                Color c = g.getColor();
                // 画背景
                g.setColor(Color.lightGray);
                g.fillRect(0, 0, getWidth(), getHeight()); // 画左、右岸
                g.setColor(new Color(208, 238, 239));
                g.fillRect(100, 0, this.getWidth() - 200, this.getHeight()); // 画河道
                // 画河道说明文字
                g.setColor(Color.blue);
                g.drawString("左岸", 60, this.getHeight() / 2 - 6);
                g.drawString("右岸", this.getWidth() - 80, this.getHeight() / 2 - 6);
                g.drawString("河道", this.getWidth() / 2 - 10,
                        this.getHeight() / 2 - 6);
    
                g.setColor(Color.red);
                g.fillRect(nx, 4, 40, 50); // 画农夫
                g.fillRect(lx, 64, 40, 50); // 画狼
                g.fillRect(bx, 124, 40, 50); // 画白菜
                g.fillRect(yx, 184, 40, 50); // 画羊
    
                // 画相应文字
                g.setColor(Color.yellow);
                g.drawString("农夫", nx + 8, 30);
                g.drawString("狼", lx + 15, 90);
                g.drawString("白菜", bx + 8, 150);
                g.drawString("羊", yx + 15, 210);
    
                g.setColor(c); // 还原颜色
            }
    
            /**
             * 开始模拟绘制动画
             * 
             * @return
             */
            public boolean startDraw() {
                if (isRun) // 是否有正在模拟的线程
                    return false;
                isRun = true; // 设置模拟开始标识
                // 初始化坐标
                nx = INITVALUE;
                lx = INITVALUE;
                bx = INITVALUE;
                yx = INITVALUE;
                // 开启线程
                Thread th = new Thread(this);
                th.start();
    
                return true;
            }
    
            /**
             * 绘制一个过程的模拟动画
             * 
             * @param loca1
             *            原状态
             * @param loca2
             *            新状态
             */
            private void drawSim(int loca1, int loca2) {
                int distance = 2; // 每次移动的像素
                int endRight = this.getWidth() - 45; // 右边结束坐标
                // 判断状态是否发生改变
                boolean nxb = FarmerProblem.farmerLocation(loca1) != FarmerProblem
                        .farmerLocation(loca2);
                boolean lxb = FarmerProblem.wolfLocation(loca1) != FarmerProblem.wolfLocation(loca2);
                boolean bxb = FarmerProblem.cabbagaLocation(loca1) != FarmerProblem
                        .cabbagaLocation(loca2);
                boolean yxb = FarmerProblem.goatLocation(loca1) != FarmerProblem.goatLocation(loca2);
    
                while (isRun) {
                    // 判断农夫在左岸还是右岸,来判断应该加或减坐标
                    if (FarmerProblem.farmerLocation(loca2)) {
                        // 农夫在左岸往右岸移动
                        nx += distance;
                        if (lxb)
                            lx += distance;
                        if (bxb)
                            bx += distance;
                        if (yxb)
                            yx += distance;
                        if (nx > endRight) // 右结束边界坐标
                            break;
                    } else {
                        // 农夫在右岸往左岸移动
                        nx -= distance;
                        if (lxb)
                            lx -= distance;
                        if (bxb)
                            bx -= distance;
                        if (yxb)
                            yx -= distance;
                        if (nx <= INITVALUE) // 左结束边界坐标
                            break;
                    }
                    repaint(); // 重绘
                    try {
                        Thread.sleep(15);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
    
            public void run() {
                System.out.println("模拟开始...");
                // 逐个取出状态进行模拟
                for (int i = 1; isRun && i < list.size(); i++) {
                    int loca1 = list.get(i - 1);
                    int loca2 = list.get(i);
                    System.out.print(loca1 + ",");
                    drawSim(loca1, loca2);
                }
                System.out.println(list.get(list.size() - 1));
                System.out.println("模拟结束...");
                isRun = false; // 设置模拟结束标识
            }
        }
    }

    运行结果:

  • 相关阅读:
    python学习---字符编码
    python学习--变量
    python学习--pycharm编辑器及优化设置
    python学习--第一个python程序helloworld
    python安装
    python学习---python2与python3的区别
    win7下weblogic安装与部署项目调试记录
    ubuntu获得root用户权限,使用xshell连接!
    linux命令
    nginx + tomcat 集群记录
  • 原文地址:https://www.cnblogs.com/wuqianling/p/5905842.html
Copyright © 2011-2022 走看看