倒油问题(深搜DFS)

    有一位厨师要从盛12斤油(a桶)的桶中倒出6斤油来,可是手边只有盛8斤油(b桶)和
盛5斤油(c桶)的两个桶,问如何操作才能将6斤取出来呢?


    一拿到这道题的时候,初想没有什么思路,后来知道广搜和深搜都可以,并且用面向对象方法的时候,特别要注意引用赋值的捆绑,以及拷贝构造(这里要非常注意需要防止捆绑),这个坑非常容易跳,一不注意就进坑了。下面提供DFS的两种面向对象方法,代码如下:  

public class Bucket {//油桶类
	public int max;// 最大油量
	public int now;// 现在的油量

	public Bucket(int max, int now) {
		this.max = max;
		this.now = now;
	}
     
	public int canIn(){//最多能倒进多少油
		return max-now;
	}
	
	public int canOut(){//最多能倒出多少油
		return now;
	}
	
	public void add(int i){//增加了多少油
		 now+=i;
	}
	
	public void out(int i){//倒出了多少油
		now-=i;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Bucket other = (Bucket) obj;
		if (max != other.max)
			return false;
		if (now != other.now)
			return false;
		return true;
	}
	
	
}


import java.util.Arrays;

public class DumpCase {
     public Bucket buckets[];
     public DumpCase parent;
	public DumpCase(Bucket[] buckets) {
		this.buckets = buckets;
	}
	//深拷贝一定要到达基本数据类型或String型赋值,否则是捆绑(浅拷贝)
    public DumpCase(DumpCase u){
    	buckets=new Bucket[u.buckets.length];
    	for(int i=0;i<buckets.length;i++){
    		buckets[i]=new Bucket(0, 0);    	
    	    buckets[i].max=u.buckets[i].max;
    	    buckets[i].now=u.buckets[i].now;
    	}	
    }
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		DumpCase other = (DumpCase) obj;
		if (!Arrays.equals(buckets, other.buckets))
			return false;
		return true;
	}
     
	
}


public class MySet {//自己写的集合
   private Object[] objs = new Object[0];
   public boolean add(Object obj){
	   if(contains(obj)){
		   return false;
	   }
	   //1创建一个新的数组,长度为原来的加1
	   Object tempObjs[] = new Object[objs.length+1];
	   //2把旧数组中的元素拷到新数组中
	   System.arraycopy(objs, 0, tempObjs, 0, objs.length);
	   //3把obj添加到新数组中
	   tempObjs[objs.length]=obj;
	   //4把objs指向新的数组
	   objs=tempObjs;
	   return true;
   }
   
   
   public Object[] getAll(){
	   return objs;
   }
   
   public boolean contains(Object obj){
	   for(Object o:objs){
		   if(o.equals(obj)){ 
			   return true;
		   }
	   }
	   return false;
   }
   
   public int size(){
	   return objs.length;
   }
   
}

import cn.hncu.myset.MySet;
 
public class oilDrumDfs {

	public static void main(String[] args) {
		Bucket buckets[] = new Bucket[3];//new一个油桶数组,分别代表三个油桶
		buckets[0] = new Bucket(12, 12);//12斤的油桶
		buckets[1] = new Bucket(8, 0);//8斤的油桶
		buckets[2] = new Bucket(5, 0);//5斤的油桶

		DumpCase u = new DumpCase(buckets);//new 一个对象u,表示三个油桶当前的状态
		u.parent = null;//父节点初始化
		
		MySet mySet = new MySet();// 用于存储所有已经搜索过的节点,若有新节点出现,则到该集合中查到看是否已经存在(实现深搜中的涂色功能)
		mySet.add(u);// 把u节点放入集合set中

		dfs(u, mySet);//进入深搜
	}

	private static void dfs(DumpCase u, MySet mySet) {
		//归鸿沟(当出现有一个桶中的油为6时,停止)
		if(u.buckets[0].now==6||u.buckets[1].now==6){
			print(u);
			return;
		} 
		
		
		// 用u的备份去倒,否则由于引用捆绑把u更改了
		DumpCase temp = new DumpCase(u);// 备份

		// 搜索temp的所有下级节点(最多9-3=6个),若没有搜过则递归深搜
		// 遍历,让桶i分别给j倒
		for (int i = 0; i < u.buckets.length; i++) {
			for (int j = 0; j < u.buckets.length; j++) {
				if (i == j) {
					// 自己给自己倒没意义,跳过
					continue;
				}

				// 程序到这里,说明i不等于j,不是同一个桶,尝试倒
				//桶i倒向j
				//先算出能倒多少: min( iOut, jIn)
				int icanDump=temp.buckets[i].canOut();
				if(icanDump>temp.buckets[j].canIn()){
					  icanDump=temp.buckets[j].canIn();
				}
				//开始倒油:倒canDump这么多
				temp.buckets[i].out(icanDump);
				temp.buckets[j].add(icanDump);
				
				//判断刚刚倒出的情况是否已经存在
				if(mySet.contains(temp)){
					//存在则还回去
					temp.buckets[i].add(icanDump);
					temp.buckets[j].out(icanDump);
					continue;
				}
				

				//如果程序能到达这里,说明上面的倒法是可以的---一种新的情况(节点)
				//把当前的节点存储到mySet中
				DumpCase v=new DumpCase(temp);//这里一定要备份temp,否则又发生捆绑
				v.parent=u;//记录父节点//这里的父节点一定是u而不是temp
				mySet.add(v);
				dfs(v,mySet);//递归深搜
				
				//让temp还原成原始状态,以让for循环继续尝试下一个邻居
				temp.buckets[i].add(icanDump);
				temp.buckets[j].out(icanDump);
			}
		}
	}
	
	public static void print(DumpCase u){
		  MySet set=new MySet();//new一个新的集合存放路径
		  set.add(u);
		  DumpCase d=u.parent;
		  while(d!=null){
			  set.add(d);
			  d=d.parent;
		  }
		//程序到这里,所有当前路径的节点都放在set中了,只是现在是倒序的
		  System.out.println("--------------------");
		//输出时再倒序一下,就变成正序
		  Object objs[]=set.getAll();
		  for(int i=objs.length-1;i>=0;i--){
			  u = (DumpCase) objs[i];
				System.out.println(u.buckets[0].now+","+u.buckets[1].now+","+u.buckets[2].now);
		  }
	} 
}

第二种(优化,重复的类就不写了):

import cn.hncu.myset.MySet;
  
public class oilDrumDfs2 {
 
	public static void main(String[] args) {
		Bucket buckets[] = new Bucket[3];//new一个油桶数组,分别代表三个油桶
		buckets[0] = new Bucket(12, 12);//12斤的油桶
		buckets[1] = new Bucket(8, 0);//8斤的油桶
		buckets[2] = new Bucket(5, 0);//5斤的油桶

		DumpCase2 u = new DumpCase2(buckets);//new 一个对象u,表示三个油桶当前的状态
		u.parent = null;//父节点初始化
		
		MySet mySet = new MySet();// 用于存储所有已经搜索过的节点,若有新节点出现,则到该集合中查到看是否已经存在(实现深搜中的涂色功能)
		mySet.add(u.toString()); //☆☆2☆☆  用特征串代替u节点
		dfs(u, mySet);//进入深搜
	}

	private static void dfs(DumpCase2 u, MySet mySet) {
		//归鸿沟(当出现有一个桶中的油为6时,停止)
		if(u.buckets[0].now==6||u.buckets[1].now==6){
			print(u);
			return;
		} 
		
		
		// 用u的备份去倒,否则由于引用捆绑把u更改了
		DumpCase2 temp = new DumpCase2(u);// 备份

		// 搜索temp的所有下级节点(最多9-3=6个),若没有搜过则递归深搜
		// 遍历,让桶i分别给j倒
		for (int i = 0; i < u.buckets.length; i++) {
			for (int j = 0; j < u.buckets.length; j++) {
				if (i == j) {
					// 自己给自己倒没意义,跳过
					continue;
				}

				// 程序到这里,说明i不等于j,不是同一个桶,尝试倒
				//桶i倒向j
				//先算出能倒多少: min( iOut, jIn)
				int icanDump=temp.buckets[i].canOut();
				if(icanDump>temp.buckets[j].canIn()){
					  icanDump=temp.buckets[j].canIn();
				}
				//开始倒油:倒canDump这么多
				temp.buckets[i].out(icanDump);
				temp.buckets[j].add(icanDump);
				
				//判断刚刚倒出的情况是否已经存在
				if(mySet.contains(temp.toString())){//☆☆3☆☆ 看看caseSet中是否存在temp的特征串
					//存在则还回去
					temp.buckets[i].add(icanDump);
					temp.buckets[j].out(icanDump);
					continue;
				}
				

				//☆☆4☆☆
				//如果程序能到达这里,说明上面的倒法是可以的---一种新的情况(节点)
				//把当前的节点的特征串存储到caseSet中----和v1版本相比,不用备份
				temp.parent = u; //记录父节点
				mySet.add(temp.toString());
				dfs(temp,mySet); //递归深搜
				
				//让temp还原成原始状态,以让for循环继续尝试下一个邻居
				temp.buckets[i].add(icanDump);
				temp.buckets[j].out(icanDump);
			}
		}
	}
	
	public static void print(DumpCase2 u){
		  MySet set=new MySet();//只存储特征串
		  set.add(u);
		  DumpCase2 d=u.parent;
		  while(d!=null){
			  set.add(d);
			  d=d.parent;
		  }
		//程序到这里,所有当前路径的节点都放在set中了,只是现在是倒序的
		  System.out.println("--------------------");
		//输出时再倒序一下,就变成正序
		  Object objs[] = set.getAll();
			for(int i=set.size()-1; i>=0;i--){
				System.out.println(objs[i]);
			}
	} 
}


import java.util.Arrays;

public class DumpCase2 {
     public Bucket buckets[];
     public DumpCase2 parent;
	public DumpCase2(Bucket[] buckets) {
		this.buckets = buckets;
	}
	//深拷贝一定要到达基本数据类型或String型赋值,否则是捆绑(浅拷贝)
    public DumpCase2(DumpCase2 u){
    	buckets=new Bucket[u.buckets.length];
    	for(int i=0;i<buckets.length;i++){
    		buckets[i]=new Bucket(0, 0);    	
    	    buckets[i].max=u.buckets[i].max;
    	    buckets[i].now=u.buckets[i].now;
    	}	
    }
	
  //☆相比v1版本,这里用特征值方法代替equals()来判断对象相等---效率更高
  	//☆☆1☆☆
  	//写一个能够标识当前对象特征的方法,,以让外面通过该特征值来进行对象相等的识别
  	@Override
  	public String toString() {
  		return buckets[0].now + ","+buckets[1].now + ","+buckets[2].now;
  	}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值