一、问题描述:
农夫要带鱼、狗、猫过河到对岸.,有一条船,只能坐一个人,农夫每次只能带一样动物过河,当农夫不在的时侯狗会咬猫,猫会吃鱼.,请问怎么顺序过河呢?
要求:编写程序,由程序来推出过河的顺序。
二、问题分析:
问题中可以提取出人、猫、狗、鱼、河、岸、船这几个对象。下面分析,这其中哪些对象是必要的,哪些是无关的。
1、河:河的存在只是为了突显左岸与右岸。农夫过河,本质上是将一堆东西从一个地方转移至另一个地方。因此,河是不必存在的;
2、岸:由1可知,设计左岸与右岸是必要的。
3、船:船是绑定于河而出现的一个模型,然而根据分析可知,问题的实质在于将一堆东西从一个地方转移至另一个地方,而转移的工具是无需描述的。因此,船不必存在。
4、人:人在整个过河问题中的作用,仅仅是为了保证人所在的岸,该岸上的动物是安全的。也就是说,人所在的位置,决定了哪一边是肯定安全的,哪一边是需要检测是否安全。人的位置只有左岸与右岸两种互斥状态。
5、猫、狗、鱼:任意一种动物都可能与另一种动物发生矛盾现象。可以设计一个动物类,并定义几个属性描述动物之间的矛盾关系。(本例中,为了简化直接将动物定为字符串类型)
6、程序执行过程中的问题:
2.1、问题的最终目标:最后左岸上的动物全部转移到了右岸
1)设计岸是为了描述一个地方与另一个地方,为了加以区分及结合情境,起名为左岸与右岸;
2)动物是待在岸上的;
3)设计两个容器,分别表示左岸与右岸。容器用于装动物;
2.2、容器的选择:数组或集合
1)数组:获取数组中已有的元素数量时非常不方便(缺点);
2)集合:获取集合中已有的元素数量时非常方便(优点);
3)总结:使用集合来描述左岸与右岸十分方便。当描述右边的集合长度为3(size()==3)时,最终目标达成。
1)设计一个boolean值flag,描述人的位置;
2)根据flag值判断出人的位置,并由此决定检测它的对岸的安全状态;
1)农夫过河一次后,若两岸存在安全问题,则需要回退到过河前的状态。
①设计一种模型,分别保存移动前的两岸各自状态,当移动后出现问题时,用于回退到移动前的状态。
2)过河过程中,需要考虑当前移动后的两岸整体状态是否曾经出现过,即该次移动是否曾经出现过,若出现过则需要回退到移动前的状态。
②设计一种模型,用于描述移动后两岸的整体状态;设计一种模型,保存所有的曾经出现过的两岸的整体状态。
三、具体代码:
/*
* 农夫过河问题
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
public class Test10 {
public static void main(String[] args) {
CrossRiver cr=new CrossRiver(); //创建一个农夫过河对象
cr.initial(); //初始化三只动物,并添加到左岸
cr.crossRiver(); //农夫开始过河
}
}
//定义一个农夫过河类
class CrossRiver{
//定义两个集合,作为左岸与右岸
private List<String> leftSide=new ArrayList<String>();
private List<String> rightSide=new ArrayList<String>();
//定义两个集合,用于分别保存每次移动前,左\右岸的状态。当移动出现问题时,用于回复之前状态
private List<String> temp1=new ArrayList<String>();
private List<String> temp2=new ArrayList<String>();
//定义一个集合,用于存储所有移动操作后的左\右岸的整体状态,每次移动后都要保存
private HashSet<ArrayList<Object>> statusSet=new HashSet<ArrayList<Object>>();
//定义一个布尔值,用于描述人的位置。当值为true时,表示人在左岸;反之,人在右岸
private Boolean flag=true;
//定义一个int值,用于记录移动的次数
private int count=1;
//声明狗、猫、鱼三只动物
private String dog;
private String cat;
private String fish;
//初始化三只动物,添加到左岸并排序
public void initial(){
dog="dog";
cat="cat";
fish="fish";
leftSide.add(dog);
leftSide.add(cat);
leftSide.add(fish);
Collections.sort(leftSide);
}
//农夫过河
public void crossRiver(){
//当右岸集合长度为3时,即3只动物均到右岸时,循环结束
while(rightSide.size()!=3){
/*判断人的位置,根据人的位置决定过河方向,
*每次过河分为农夫独自过河与农夫带一只动物过河两种情况
*/
if(flag){
if(moveAlone(leftSide,rightSide))//农夫独自过河
continue; //当农夫独自过河是安全的,就结束本次循环
moveWithAnimal(leftSide,rightSide);
}else{
if(moveAlone(rightSide,leftSide))
continue;
moveWithAnimal(rightSide,leftSide);
}
}
System.out.println("农夫成功过河!");
}
//农夫独自过河
private boolean moveAlone(List<String> fromSide,List<String> toSide) {
System.out.println("第"+(count++)+"次移动:");
store(); //保存移动前状态
if(fromSide==leftSide)
System.out.println("人单独从左岸到右岸");
else
System.out.println("人单独从右岸到左岸");
flag=!flag; //移动后,改变人的位置状态
System.out.println("本次移动后状态为:"+leftSide+","+rightSide+","+flag);
return isSafe(fromSide); //判断本次移动是否安全,并返回判断值
}
//农夫带着一只动物过河
private void moveWithAnimal(List<String> fromSide,List<String> toSide) {
//从出发的岸上,依次选择带一只动物过河
for (int i = 0; i < fromSide.size(); i++) {
System.out.println("第"+(count++)+"次移动:");
store();
String animal=fromSide.get(i); //将动物从此岸运往彼岸
fromSide.remove(animal);
toSide.add(animal);
if(fromSide==leftSide)
System.out.println("人带着"+animal+"从左岸到右岸");
else
System.out.println("人带着"+animal+"从右岸到左岸");
flag=!flag;
System.out.println("本次移动后状态为:"+leftSide+","+rightSide+","+flag);
if(isSafe(fromSide)) //判断本次移动是否安全,若安全则结束for循环;否则继续带另外的动物移动
return;
else
continue;
}
}
//保存每次移动前,左\右岸的状态
private void store() {
copy(temp1, leftSide);
copy(temp2, rightSide);
System.out.println("本次移动前状态为:"+leftSide+","+rightSide+","+flag);
}
//集合的拷贝
private void copy(List<String> newSet,List<String> oldSet){
newSet.clear(); //拷贝前,先清空之前的内容
newSet.addAll(oldSet);
}
//当移动出现问题时,用于回复之前状态
private void recover() {
copy(leftSide, temp1);
copy(rightSide, temp2);
flag=!flag;
System.out.println("回复为本次移动前状态:"+leftSide+","+rightSide+","+flag);
}
//判断此次移动是否安全成功
private boolean isSafe(List<String> side) {
//判断此次移动是否曾经出现过
if(addStatus()){
return checkSide(side);
}else{
System.out.println("本次移动曾经出现过");
recover();
return false;
}
}
//检测此次移动后,某一岸是否安全
private boolean checkSide(List<String> side) {
//若动物数量少于两只,则该岸一定安全
if(side.size()<2){
System.out.println("本次移动是安全的");
return true;
}
if(checkSafe(side)){
System.out.println("本次移动是安全的");
return true;
}else{
System.out.println("本次移动是不安全的");
recover();
return false;
}
}
//具体检测某一岸的安全状态
private boolean checkSafe(List<String> side) {
return !((side.contains("cat")&&side.contains("dog"))||(side.contains("cat")&&side.contains("fish")));
}
//将两岸的整体状态添加进集合中,并判断这次移动操作是否曾经出现过
private boolean addStatus() {
//将集合排序,用于保证动物移动过程中产生的[dog,cat]与[cat,dog]是同一种状态
Collections.sort(leftSide);
Collections.sort(rightSide);
//定义一个集合,用于表示移动后两岸的整体状态
ArrayList<Object> status=new ArrayList<Object>();
status.add(leftSide);
status.add(rightSide);
status.add(flag);
return statusSet.add(status);
}
}