狼羊过河问题
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();
}
}
打印结果如下:
方案:(共11步)
第1步, 前进---> 狼2,羊0
第2步, 后退---> 狼1,羊0
第3步, 前进---> 狼2,羊0
第4步, 后退---> 狼1,羊0
第5步, 前进---> 狼0,羊2
第6步, 后退---> 狼1,羊1
第7步, 前进---> 狼0,羊2
第8步, 后退---> 狼1,羊0
第9步, 前进---> 狼2,羊0
第10步, 后退---> 狼1,羊0
第11步, 前进---> 狼2,羊0