package guohe; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * 狼羊过河问题 * @author tiger * @date 2011年1月27日。 * * 这里是用面向对象的编程方式,有定义了狼和羊两个类。 * 但其实程序里并没有用到它们的行为,该问题只是关心它们的数目而已! * 所以其实可以直接就定义四个变量来存储两岸的狼数和羊数,然后在遍历 * 递归的同时增减这四个数字即可。 * */ public class Guohe { //左岸的狼数和羊数 private List<Actor> leftLangs = new LinkedList<Actor>(); private List<Actor> leftYangs = new LinkedList<Actor>(); //右岸的狼数和羊数 private List<Actor> rightLangs = new LinkedList<Actor>(); private List<Actor> rightYangs = new LinkedList<Actor>(); //存储所有成功过河的方案 private List winSteps = new ArrayList(); public Guohe() { init(); } /** * 刷新重置盘面为初始情况 */ private void init() { leftLangs.clear(); leftYangs.clear(); rightLangs.clear(); rightYangs.clear(); for (int i = 0; i < 3; i++) { leftLangs.add(new Lang()); leftYangs.add(new Yang()); } //这两个状态需要初始放进去。 states.add("3,3,0,0,1"); states.add("3,3,0,0,2"); //方向设为初始向对岸的方向 fangxiang = TYPE_FANGXIANG_GO; } //到对岸:go , 回来:come private static int TYPE_FANGXIANG_GO = 1; private static int TYPE_FANGXIANG_COME = 2; //船行方向 private int fangxiang = TYPE_FANGXIANG_GO; /** * 保存盘面的状态 * * 保存的是有5个数字的字符串,这5个数字代表的意义依次是: * 左岸狼数、左岸羊数、右岸狼数、右岸羊数、当前船行方向。 * * 状态的保存非常重要,把所有递归遍历遇到的状态都保存在这里, * 当后面的某次遍历所遇到的盘面已经有在states里存在的话,说明该 * 盘面已经被处理过,则可以直接跳过。 * * 保存下已经处理过的盘面,这样的设计可以防止出现死循环导致内存泄露。 */ private List states = new ArrayList(); /** * 针对当前船行方向,根据当前盘面情况,即两岸的狼数和羊数 * 得到可以上船的狼数和羊数。 该方法中会把盘面状态保存入states. * @param fangxiang * @return */ private List<int[]> getChuanMember(int fangxiang) { List<int[]> rtnlist = new LinkedList<int[]>(); String key = ""; List alangs, ayangs, blangs, byangs; if(fangxiang == TYPE_FANGXIANG_GO) { alangs = leftLangs; ayangs = leftYangs; blangs = rightLangs; byangs = rightYangs; }else{ alangs = rightLangs; ayangs = rightYangs; blangs = leftLangs; byangs = leftYangs; } //判断 1,0 .(1狼0羊) if(alangs.size() >= 1) { if((alangs.size() - 1 <= ayangs.size() || ayangs.size() == 0) && (blangs.size() + 1 <= byangs.size() || byangs.size() == 0)) { if(fangxiang == TYPE_FANGXIANG_GO) { key = (alangs.size() - 1) + "," + ayangs.size() + "," + (blangs.size() + 1) + "," + byangs.size() + "," + TYPE_FANGXIANG_GO; }else{ key = (blangs.size() + 1) + "," + byangs.size() + "," + (alangs.size() - 1) + "," + ayangs.size() + "," + TYPE_FANGXIANG_COME; } if(!states.contains(key)) { rtnlist.add(new int[]{1, 0}); states.add(key); } } } //判断 2,0 .(2狼0羊) if(alangs.size() >= 2) { if((alangs.size() - 2 <= ayangs.size() || ayangs.size() == 0) && (blangs.size() + 2 <= byangs.size() || byangs.size() == 0)) { if(fangxiang == TYPE_FANGXIANG_GO) { key = (alangs.size() - 2) + "," + ayangs.size() + "," + (blangs.size() + 2) + "," + byangs.size() + "," + TYPE_FANGXIANG_GO; }else{ key = (blangs.size() + 2) + "," + byangs.size() + "," + (alangs.size() - 2) + "," + ayangs.size() + "," + TYPE_FANGXIANG_COME; } if(!states.contains(key)) { rtnlist.add(new int[]{2, 0}); states.add(key); } } } //判断0,1 .(0狼1羊) if(ayangs.size() >= 1) { if((alangs.size() <= ayangs.size() - 1 || ayangs.size() - 1 == 0) && (blangs.size() <= byangs.size() + 1 || byangs.size() + 1 == 0)) { if(fangxiang == TYPE_FANGXIANG_GO) { key = alangs.size() + "," + (ayangs.size() - 1) + "," + blangs.size() + "," + (byangs.size() + 1) + "," + TYPE_FANGXIANG_GO; }else{ key = blangs.size() + "," + (byangs.size() + 1) + "," + alangs.size() + "," + (ayangs.size() - 1) + "," + TYPE_FANGXIANG_COME; } if(!states.contains(key)) { rtnlist.add(new int[]{0, 1}); states.add(key); } } } //判断0,2 .(0狼2羊) if(ayangs.size() >= 2) { if((alangs.size() <= ayangs.size() - 2 || ayangs.size() - 2 == 0) && (blangs.size() <= byangs.size() + 2 || byangs.size() + 2 == 0)) { if(fangxiang == TYPE_FANGXIANG_GO) { key = alangs.size() + "," + (ayangs.size() - 2) + "," + blangs.size() + "," + (byangs.size() + 2) + "," + TYPE_FANGXIANG_GO; }else{ key = blangs.size() + "," + (byangs.size() + 2) + "," + alangs.size() + "," + (ayangs.size() - 2) + "," + TYPE_FANGXIANG_COME; } if(!states.contains(key)) { rtnlist.add(new int[]{0, 2}); states.add(key); } } } //判断1,1 .(1狼1羊) if(alangs.size() >= 1 && ayangs.size() >= 1) { if((alangs.size() - 1 <= ayangs.size() - 1 || ayangs.size() - 1 == 0) && (blangs.size() + 1 <= byangs.size() + 1 || byangs.size() + 1 == 0)) { if(fangxiang == TYPE_FANGXIANG_GO) { key = (alangs.size() - 1) + "," + (ayangs.size() - 1) + "," + (blangs.size() + 1) + "," + (byangs.size() + 1) + "," + TYPE_FANGXIANG_GO; }else{ key = (blangs.size() + 1) + "," + (byangs.size() + 1) + "," + (alangs.size() - 1) + "," + (ayangs.size() - 1) + "," + TYPE_FANGXIANG_COME; } if(!states.contains(key)) { rtnlist.add(new int[]{1, 1}); states.add(key); } } } return rtnlist; } /** * 用来存储当前遍历的方案步骤 * * 存储的是大小为3的数字数组。各元素的意义依次是: * 船行方向、船上狼数、船上羊数 * */ private List<int[]> temp = new ArrayList<int[]>(); /** * 递归方法,不断深度遍历 */ private void digui() { //得到当前盘面下,可以上船的狼数和羊数 List list = this.getChuanMember(fangxiang); //如果找不到可乘船的狼羊,则说明当前的盘面无法走向成功,则退出。 if(list.isEmpty()) { //重置为初始盘面情况 init(); //清空之前的步骤存储 temp.clear(); return; } for (int i = 0; i < list.size(); i++) { int[] element = (int[]) list.get(i); if(fangxiang == TYPE_FANGXIANG_GO) { //修改两岸的狼数 if(element[0] == 1) { Actor a = this.leftLangs.remove(0); this.rightLangs.add(a); } else if(element[0] == 2) { Actor a = this.leftLangs.remove(0); this.rightLangs.add(a); a = this.leftLangs.remove(0); this.rightLangs.add(a); } //修改两岸的羊数 if(element[1] == 1) { Actor a = this.leftYangs.remove(0); this.rightYangs.add(a); } else if(element[1] == 2) { Actor a = this.leftYangs.remove(0); this.rightYangs.add(a); a = this.leftYangs.remove(0); this.rightYangs.add(a); } //存储步骤 temp.add(new int[]{fangxiang, element[0], element[1]}); //判断是否完成,如果完成,将完成步骤加入完成方案的列表里 if(this.success()) { //打印成功方案 System.out.println("方案:(共" + temp.size() + "步)"); for (int j = 0; j < temp.size(); j++) { int[] a = (int[]) temp.get(j); System.out.println("第" + (j + 1) + "步, " + (a[0] == 1 ? "前进" : "后退") + "---> 狼" + a[1] + ",羊" + a[2]); } winSteps.add(temp); //重置盘面为初始情况 init(); //清空之前的步骤存储 temp.clear(); return; //这里是return而非continue,是因为既然已经成功了,而当前是最后一步,那么后面的循环必然不能成功。--->不大肯定。 } fangxiang = TYPE_FANGXIANG_COME; digui(); } else if(fangxiang == TYPE_FANGXIANG_COME) { //修改两岸的狼数 if(element[0] == 1) { Actor a = this.rightLangs.remove(0); this.leftLangs.add(a); } else if(element[0] == 2) { Actor a = this.rightLangs.remove(0); this.leftLangs.add(a); a = this.rightLangs.remove(0); this.leftLangs.add(a); } //修改两岸的羊数 if(element[1] == 1) { Actor a = this.rightYangs.remove(0); this.leftYangs.add(a); } else if(element[1] == 2){ Actor a = this.rightYangs.remove(0); this.leftYangs.add(a); a = this.rightYangs.remove(0); this.leftYangs.add(a); } //存储步骤 temp.add(new int[]{fangxiang, element[0], element[1]}); //调换方向 fangxiang = TYPE_FANGXIANG_GO; //递归处理现在的盘面 digui(); } } } private boolean success() { return this.leftLangs.isEmpty() && this.leftYangs.isEmpty() && this.rightLangs.size() == 3 && this.rightYangs.size() == 3; } public static final int TYPE_LANG = 1; public static final int TYPE_YANG = 2; class Actor { public int type = 0; public boolean isLang() { return type == TYPE_LANG; } public boolean isYang() { return type == TYPE_YANG; } } //狼 class Lang extends Actor{ public Lang() { type = TYPE_LANG; } } //羊 class Yang extends Actor{ public Yang() { type = TYPE_YANG; } } public static void main(String[] args) { new Guohe().digui(); } }