有一位厨师要从盛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]); } }
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; } }
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; } }
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; } } }