import cn.hncu.sreach.putOil.common.Bucket; import cn.hncu.sreach.putOil.common.DumpCase; import cn.hncu.sreach.putOil.common.MySet; /* 有一位厨师要从盛12斤油(a桶)的桶中倒出6斤油来,可是手边只有盛8斤油(b桶)和盛5斤油(c桶)的两个桶,问如何操作才能将6斤取出来呢? * 本题采用广度优化搜索解题:每倒一次油,就有可能有6种方案,每倒一次就判断有没有出现6斤的情况,若出现就记录下来,然后继续广搜,直到所有的情况全部搜完。 * 下面有详细的注释。 */ public class DumpOilBFS { public static void main(String[] args) { Bucket[] buckets = new Bucket[3]; // new数组长度为3,表示3个桶 buckets[0] = new Bucket(12, 12); // 第一桶的属性,桶的大小为12斤,现有12斤 buckets[1] = new Bucket(8, 0); // 第一桶的属性,桶的大小为8斤,现有0斤 buckets[2] = new Bucket(5, 0); // 第一桶的属性,桶的大小为5斤,现有0斤 DumpCase u = new DumpCase(buckets); // new DuampCase,用来表示现在三个桶的状态 MySet caseSet = new MySet(); // new MySet,用来表示已经出现过的三个桶的状态,后面用来防止走重复的步骤 caseSet.add(u); CaseQueue que = new CaseQueue(); // 队列,用来把当前的三个桶的状态和此时caseSet的状态装进去 que.enqueue(u, caseSet); bfs(que); // 进入广度优化搜索 } // 广度优化搜索 private static void bfs(CaseQueue que) { while (!que.isEmpty()) { // 如果队列为空,直接退出 Q q = que.dequeque(); // 从队列中取出一个元素q DumpCase u = que.getDumpCase(q); // 从q中提取出DumpCase MySet caseSet = que.getMySet(q); // 从q中提取出DumpCase // 判断是否出现了某一个桶中的油为6斤,退出循环 if (u.buckets[0].now == 6 || u.buckets[1].now == 6) { print(u, caseSet); // 输出路径 continue; // 此处不能break,不然只会出来一种情况 // break; } DumpCase temp = new DumpCase(u);// 此处备份一次,以便后面的还原 // 此处是广搜的核心 for (int i = 0; i < temp.buckets.length; i++) { for (int j = 0; j < temp.buckets.length; j++) { if (i == j) {//不能自己和自己倒 continue; } int inCanDump = temp.buckets[i].canOut(); //看这次能倒多少 if (inCanDump > temp.buckets[j].canIn()) { //判断能倒的量是否大于接收的量 inCanDump = temp.buckets[j].canIn(); //如果大于的话,那么能把i中的全部倒出来 } if (inCanDump == 0) { //如果倒进去 continue; } //开始倒油,把该减的减了,该加的加了 temp.buckets[i].out(inCanDump); temp.buckets[j].in(inCanDump); //判断倒完之后以前是否出现过这样的情况 if (caseSet.contains(temp)) { //若存在,则还原 temp.buckets[i].in(inCanDump); temp.buckets[j].out(inCanDump); continue; } DumpCase v = new DumpCase(temp); v.parent = u; //记录父节点 caseSet.add(v);//记录到已经搜索的结点集合当中 que.enqueue(v, caseSet);//加入到广搜队列当中 //必须还原,以便从该结点继续尝试其它路径 temp.buckets[i].in(inCanDump); temp.buckets[j].out(inCanDump); } } } } //输出路径(按倒序输出来),下一个需要输出的是上一个的父节点 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; } Object[] obj = set.getAll(); for (int i = obj.length - 1; i >= 0; i--) { if (i > 0) { System.out.print(obj[i] + " --->>"); } else { System.out.println(obj[i]); } } } } //用来装DumpCase和MySet的对象 class Q { public DumpCase d; public MySet m; //构造函数 public Q(DumpCase d, MySet m) { this.d = d; this.m = new MySet(m); } public DumpCase getD() { return d; } public MySet getM() { return m; } } //队列(队列中元素是捆绑了DumpCase和MySet的对象) class CaseQueue { private Q[] qs = new Q[100]; DumpCase u = null; MySet set = null; int end = 0; //队列指针,表示队列中元素个数 // 入队列 public int enqueue(DumpCase u, MySet caseSet) { qs[end++] = new Q(u, caseSet); return end; } public DumpCase getDumpCase(Q q) { return q.d; } public MySet getMySet(Q q) { return q.m; } // 出队列 public Q dequeque() { if (isEmpty()) { return null; } Q q = qs[0]; //每次都是弹出队列的第一个元素 //所有元素往前面移一位 if (end > 1) { for (int i = 0; i < end; i++) { qs[i] = qs[i + 1]; } } end--; return q; } //判断是否为空 public boolean isEmpty() { if (end == 0) { return true; } return false; } }
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; } }
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]; //把u中的元素一个个拷贝到新的buckets中 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; return true; } }class MySet:
public class MySet { private Object[] objs = new Object[0]; // 拷贝构造(防止捆绑)这里最开始写的时候是没有的,因为后面调试发现不能把全部的路径数出来, // 这样是把每次加入队列时把当前的caseSet的状态也拷贝进去,不过这样修改后还是没有输出所有的值 public MySet(MySet m) { objs = new Object[m.getAll().length]; for (int i = 0; i < objs.length; i++) { objs[i] = new Object(); objs[i] = m.getAll()[i]; } } public MySet() { } // 添加函数 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; } // 获取MySet中所有的元素 public Object[] getAll() { return objs; } // 获取MySet的元素个数 public int size() { return objs.length; } }
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/xionghui2013/article/details/47344455