倒油问题详解 (深搜、广搜)(面向过程和面向对象)

题目:

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

思路:

思路其实很容易理解,就是三个桶之间互相倒油,直到倒出想要的结果,也就是其中任意一个桶中出现6即可。难就难在,如果直接让三个桶互相倒的话,很容易出现死循环,也就是a倒到b,下一步的时候,有让b倒回到a,所以要防止这种

情况的出现,才能找到结果。解决方法:先倒,然后看看三个桶中容量的状态,和前面的三个桶的状态是否有相同,若有,则不让这步进行,没有,则可以倒油。

实现代码:

面向过程的方法:

public class PourOil {
	public static int[] a = { 12, 8, 5 };//三个桶的容量
	public static int count=0;//记录到处方法的个数
	public static void main(String[] args) {
		int[][] f = new int[100][3];//记录三个桶内容量的变化
		f[0][0] = 12;//三个桶初始容量为 12 0 0 
		f[0][1] = 0;
		f[0][2] = 0;
		DFS(f, 1);//通过深搜寻找下一桶内容量
	}
	//深搜
	private static void DFS(int[][] f, int x) {
		if (x > 100) {//控制最多倒油的次数
			return;
		}
		if (f[x - 1][0] == 6 || f[x - 1][1] == 6 || f[x - 1][2] == 6) {//只要三个桶中,任意一个桶容量出现6,表示达到目的,退出递归
			count++;
			System.out.print("方法"+count+":");
			print(f, x );//输出倒油的过程
			return;
		}
		for (int i = 0; i < 3; i++) {//三个桶之间互相倒油
			for (int j = 0; j < 3; j++) {
				if (isTrue(f, x, i, j)) {//能倒油的条件
					DFS(f,x+1);//倒油成功,寻找下一个倒油方法
				}
			}
		}
	}

	private static boolean isTrue(int[][] f, int x, int i, int j) {
		if (f[x - 1][i] == 0) {//要倒油的桶容量为0,则不能倒油
			return false;
		}
		if (f[x - 1][j] == a[j]) {//接收油的桶已满,则不能倒油
			return false;
		}
		if (i == j) {//自己不能与自己互相倒油
			return false;
		}
		pourOil(f, x, i, j);//倒油
		for (int t = 0; t < x; t++) {//判断在前面是否出现了这种状态,若出现,则不必再倒成这种状态,防止死循环(开始就遗漏了这个,进入死循环了呜呜呜呜)
			if (f[t][0] == f[x][0] && f[t][1] == f[x][1] && f[t][2] == f[x][2]) {
				return false;
			}
		}
		return true;
	}

	private static void pourOil(int[][] f, int x, int i, int j) {
		f[x][0] = f[x - 1][0];
		f[x][1] = f[x - 1][1];
		f[x][2] = f[x - 1][2];
		if (f[x - 1][i] > a[j] - f[x - 1][j]) {//倒油的桶中的容量,大于被倒桶中剩余的容量
			f[x][i] = f[x - 1][i] - (a[j] - f[x - 1][j]);
			f[x][j] = a[j];
		} else {//被倒的桶能装下倒油桶内的所有油
			f[x][j] += f[x][i];
			f[x][i] = 0;
		}
	}

	private static void print(int[][] f, int x) {
		for (int i = 0; i < x-1; i++) {
			System.out.print(f[i][0] + "," + f[i][1] + "," + f[i][2] + " ---> ");
		}
		System.out.println(f[x-1][0] + "," + f[x-1][1] + "," + f[x-1][2]);
	}

}


面向对象的方法:

公共类:

MySet.java(自己写的集合)

package search.oil.common;

public class MySet {
	private Object[] objs=new Object[0];
	public boolean add(Object obj){
		if(contains(obj)){
			return false;
		}
		Object tempObjs[] = new Object[objs.length+1];
		System.arraycopy(objs, 0, tempObjs, 0, objs.length);
		tempObjs[objs.length] = obj;
		objs = tempObjs;
		return true;
	}
	
	public boolean contains(Object obj){
		for(Object o:objs){
			if(o.equals(obj)){
				return true;
			}
		}
		return false;
	}
	public Object[] getAll(){
		return objs;
	}
	public int size(){
		return objs.length;
	}
	
}


Bucket.java(桶类)
package search.oil.common;

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 in( int a){
		now +=a;
	}
	public void out(int a){
		now -=a;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + max;
		result = prime * result + now;
		return result;
	}
	@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;
	}
	
}


DumpCase.java(存放路径的类)
package search.oil.common;

import java.util.Arrays;

public class DumpCase {
	public Bucket buckets[] =null;
	public DumpCase parent=null;
	public DumpCase( Bucket buckets[]){
		this.buckets = buckets;
	}
	public DumpCase(DumpCase u) {
		buckets = new Bucket[u.buckets.length];
		for(int i=0;i<u.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 String toString() {
		return "A=" +buckets[0].now+ " B="+buckets[1].now+ " C="+buckets[2].now;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + Arrays.hashCode(buckets);
		return result;
	}
	@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;
		/*
		for(int i=0;i<buckets.length;i++){
			if( buckets[i].now != other.buckets[i].now){
				return false;
			}
		}*/
		
		return true;
	}
	
}


深搜:

package search.oil.dfs;

import search.oil.common.Bucket;
import search.oil.common.DumpCase;
import search.oil.common.MySet;

public class DumpOilDFS1 {

	public static void main(String[] args) {
		Bucket buckets[] = new Bucket[3];
		buckets[0] = new Bucket(12,12);
		buckets[1] = new Bucket(8,0);
		buckets[2] = new Bucket(5,0);
		
		DumpCase u = new DumpCase(buckets);
		MySet caseSet = new MySet();
		caseSet.add(u);
		
		dfs(u,caseSet);
		
	}
	
	/**输出搜索路径*/
	public static void print(DumpCase u, MySet caseSet){
		MySet set = new MySet();
		set.add(u);
		DumpCase d = u.parent;
		while(d!=null){
			set.add(d);
			d = d.parent;
		}
		System.out.println("--------------");
		Object objs[] = set.getAll();
//		for(Object obj: objs){
//			System.out.println(obj);
//		}
		for(int i=objs.length-1; i>=0;i--){
			System.out.println(objs[i]);
		}
	}
	
	
	private static void dfs(DumpCase u, MySet caseSet) {
		//鸿沟
		if(u.buckets[0].now==6 || u.buckets[1].now==6){
			//System.out.println(u);
			print(u,caseSet);
			return;
		}
		
		//先备份u,然后用备份体来进行搜索
		DumpCase temp = new DumpCase(u);
		//遍历 (桶i向j倒)
		for(int i=0; i<temp.buckets.length;i++){
			for(int j=0; j<temp.buckets.length;j++){
				//自己不能给自己倒
				if(i==j){
					continue;//跳过
				}
				
				//算出能倒多少,存储在变量iCanDump中
				int iCanDump = temp.buckets[i].canOut();
				if(iCanDump>temp.buckets[j].canIn()){
					iCanDump = temp.buckets[j].canIn();
				}
				//开始倒
				temp.buckets[i].out(iCanDump);
				temp.buckets[j].in(iCanDump);
				
				//先判断这种情况在caseSet中是否已经存在。若存在则还回去,搜索下一种情况
				if(caseSet.contains(temp)){
					//还回去
					temp.buckets[i].in(iCanDump);
					temp.buckets[j].out(iCanDump);
					continue;
				}
				
				//再做一次备份,把备份体加到集合当中,继续搜索
				DumpCase v = new DumpCase(temp);
				v.parent = u;//记录父结点
				caseSet.add(v);
				dfs(v,caseSet);
				//还原现场
				temp.buckets[i].in(iCanDump);
				temp.buckets[j].out(iCanDump);
				//temp.parent = null;//可以省略
			}
		}
		
	}

}

广搜:

package search.oil.bfs;

import search.oil.common.Bucket;
import search.oil.common.DumpCase;
import search.oil.common.MySet;

public class DumpOilBFS {

	public static void main(String[] args) {
		Bucket buckets[] = new Bucket[3];
		buckets[0] = new Bucket(12,12);
		buckets[1] = new Bucket(8,0);
		buckets[2] = new Bucket(5,0);
		DumpCase u = new DumpCase(buckets);
		MySet caseSet = new MySet();
		caseSet.add(u);
		CaseQueue que = new CaseQueue();
		que.enqueue(u);
		bfs(que,caseSet);
	}

	private static void bfs(CaseQueue que, MySet caseSet) {
		while(!que.isEmpty()){
			DumpCase u = que.dequeque();
			//找到答案了
			if(u.buckets[0].now==6 || u.buckets[1].now==6){
				//System.out.println(u);
				print(u,caseSet);
				continue;//return;
			}
			
			DumpCase temp = new DumpCase(u); //拷贝一份进行倒
			//遍历 倒的方向:i-->j
			for(int i=0;i<temp.buckets.length;i++){
				for(int j=0;j<temp.buckets.length;j++){
					if(i==j){
						continue;
					}
					
					//看看这次能倒多少  iCanDump
					int iCanDump = temp.buckets[i].canOut();
					if(temp.buckets[j].canIn() < iCanDump){
						iCanDump = temp.buckets[j].canIn();
					}
					//等于0,不用倒
					if(iCanDump==0){
						continue;
					}
					
					//开始倒
					temp.buckets[i].out(iCanDump);
					temp.buckets[j].in(iCanDump);
					
					//判断是否已经存在
					if(caseSet.contains(temp)){
						//还回去
						temp.buckets[j].out(iCanDump);
						temp.buckets[i].in(iCanDump);
						continue;
					}
					
					//到这里,说明这种倒法是可以的,而且之前没出现过,可以从此结点搜索下去
					//对这种情况进行记录,注意要拷贝一份新的存入集体,否则会和下一次循环发现捆绑
					DumpCase v = new DumpCase(temp);
					v.parent = u;
					caseSet.add(v); //记录到已经搜索的结点集合当中
					que.enqueue(v);//加入到广搜队列当中
					
					//必须还原,以便从该结点继续尝试其它路径
					temp.buckets[j].out(iCanDump);
					temp.buckets[i].in(iCanDump);
					
				}
			}
			
		}
		
	}

	private static void print(DumpCase u, MySet caseSet) {
		MySet set = new MySet();
		set.add(u);
		DumpCase d = u.parent;
		while(d!=null){
			set.add(d);
			d = d.parent;
		}
		System.out.println("-----------");
		Object objs[] = set.getAll();
		for(int i=objs.length-1;i>=0;i--){
			System.out.println(objs[i]);
		}
	}

}

class CaseQueue{
	private DumpCase cases[] = new DumpCase[100];
	int end=0;
	
	//入队列
	public int enqueue(DumpCase u){
		cases[end++] = u;
		return end;
	}
	
	//出队列
	public DumpCase dequeque(){
		if(isEmpty()){
			return null;
		}
		DumpCase u = cases[0];
		if(end>1){
			for(int i=0;i<end;i++){
				cases[i] = cases[i+1];
			}
		}
		end--;
		return u;
	}

	public boolean isEmpty() {
		if(end==0){
			return true;
		}else{
			return false;
		}
	}
	
}










你可能感兴趣的:(java,倒油问题)