农夫过河问题

一、问题描述:

农夫要带鱼、狗、猫过河到对岸.,有一条船,只能坐一个人,农夫每次只能带一样动物过河,当农夫不在的时侯狗会咬猫,猫会吃鱼.,请问怎么顺序过河呢?

要求:编写程序,由程序来推出过河的顺序。

二、问题分析:

问题中可以提取出人、猫、狗、鱼、河、岸、船这几个对象。下面分析,这其中哪些对象是必要的,哪些是无关的。

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);		
	}
}


四、延伸阅读:其他实现农夫过河的方法

1、农夫过河问题实现

2、农夫过河问题(深度优先算法)java版

3、农夫过河问题(java实现)

4、 java编程解决猫狗鱼过河问题

5、农夫过河问题-呆呆沙师妹的解答

6、农夫过河问题

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值