倒油问题之深搜和广搜算法

来源:互联网 发布:mac连上wifi打不开网页 编辑:程序博客网 时间:2024/04/28 05:12

   问题是这样的,有一位厨师他有3个油桶 分别可以装12 ,8,5斤油

   现在12斤的油桶是满的,其他的是空的,问:如何倒出6斤油?




   对于这个问题,开始我也是很迷茫的,不知道怎么弄!后来才知道用深搜和广搜来做!

下面附上我的代码,如有不妥请多包涵



首先我们定义一个油桶类 Bucket

//油桶
public class Bucket {
int max;// 最大值
int now;// 现在有多少油


public Bucket(int max, int now) {
this.max = max;
this.now = now;
}


public int cIn() {// 现在可以装多少油
return max - now;
}


public int cOut() {// 现在可以倒多少油
return now;
}


public void in(int a) {// 进油
now += a;
}


public void out(int a) {// 倒油
now -= a;
}


@Override
public String toString() {
return "Bucket [max=" + max + ", now=" + now + "]";
}


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


}


再定义一个DrumCase类 用来专门记录油桶

import java.util.Arrays;


public class DrumCase {
public Bucket[] buckets = null;
public DrumCase parent = null;// 父节点


public DrumCase(Bucket[] bucket) {
this.buckets = bucket;
}


// 备份构造函数
public DrumCase(DrumCase 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);
result = prime * result + ((parent == null) ? 0 : parent.hashCode());
return result;
}


@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
DrumCase other = (DrumCase) obj;


if (!Arrays.equals(buckets, other.buckets))
return false;


return true;
}


}

然后,我又自己写了一个MySet()集合函数

package PourTheOil;


//集合
public class MySet {
private Object[] objs = new Object[0];


public boolean add(Object obj) {// 加对象
if (contan(obj)) {
return false;
}
Object[] temp = new Object[objs.length + 1];// 每次加一个对象,每次数组长度加一
System.arraycopy(objs, 0, temp, 0, objs.length);
temp[objs.length] = obj;
objs = temp;
return true;
}


public boolean contan(Object obj) {// 判断对象是否存在
for (Object o : objs) {
if (o.equals(obj)) {
return true;
}
}
return false;
}


public Object[] getAll() {// 获取集合中的所有对象
return objs;
}


}

前面几个类都是通用的,下面就是 算法了

首先是dfs版本一

package PourTheOil;


public class OilDFS1 {


public static void main(String[] args) {
Bucket bucket[] = new Bucket[3];
// 初始化油桶
bucket[0] = new Bucket(12, 12);
bucket[1] = new Bucket(8, 0);
bucket[2] = new Bucket(5, 0);


DrumCase u = new DrumCase(bucket);// 把油桶数组加入进去
MySet caseSet = new MySet();
caseSet.add(u);// 把数组加入到集合
dfs(u, caseSet);// 开始搜索
}


private static void dfs(DrumCase u, MySet caseSet) {
// 鸿沟
if (u.buckets[0].now == 6 || u.buckets[1].now == 6) {
print(u, caseSet);// 輸出
return;
}


// 备份,用备份体来遍历,防止捆绑
DrumCase temp = new DrumCase(u);
// 遍历
for (int i = 0; i < temp.buckets.length; i++) {
for (int j = 0; j < temp.buckets.length; j++) {
if (i == j) {
// 自己不遍历
continue;
}


// 定义一个变量来看能倒多少油
int iCanDrump = temp.buckets[i].cOut();
if (iCanDrump > temp.buckets[j].cIn()) {
iCanDrump = temp.buckets[j].cIn();
}


// 开始倒油
temp.buckets[i].out(iCanDrump);
temp.buckets[j].in(iCanDrump);


// 判斷MySet中是否存在


if (caseSet.contan(temp)) {
// 倒回去
temp.buckets[j].out(iCanDrump);
temp.buckets[i].in(iCanDrump);
continue;
}


// 再備份一個,來搜索
DrumCase v = new DrumCase(temp);
v.parent = u;
caseSet.add(v);
dfs(v, caseSet);
// 還原現場
temp.buckets[j].out(iCanDrump);
temp.buckets[i].in(iCanDrump);


}


}


}


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

-----------------------------------------------

dfs2版本:

package PourTheOil;


public class CopyOfOilDFS2 {
/**
*  该版本的设计思想:集合容器中存放“特征字符串”,通过调用对象的toString()方法来判断该结点是否已经存在。
*         用一个与对象对应的“记号”来代替对象本身,以简化程序的匹配与修改的复杂度。——类似Hash的思想
*/


public static void main(String[] args) {
Bucket bucket[] = new Bucket[3];
// 初始化油桶
bucket[0] = new Bucket(12, 12);
bucket[1] = new Bucket(8, 0);
bucket[2] = new Bucket(5, 0);


DrumCase u = new DrumCase(bucket);// 把油桶数组加入进去
MySet caseSet = new MySet();
caseSet.add(u.toString());// 把数组加入到集合,//(1)往集合中存放“特征串”,此处也可另外单独用一个hash()方法来代替toString()
dfs(u, caseSet);// 开始搜索,
}


private static void dfs(DrumCase u, MySet caseSet) {
// 鸿沟
if (u.buckets[0].now == 6 || u.buckets[1].now == 6) {
print(u, caseSet);// 輸出
return;
}


// 备份,用备份体来遍历,防止捆绑
DrumCase temp = new DrumCase(u);
// 遍历
for (int i = 0; i < temp.buckets.length; i++) {
for (int j = 0; j < temp.buckets.length; j++) {
if (i == j) {
// 自己不遍历
continue;
}


// 定义一个变量来看能倒多少油
int iCanDrump = temp.buckets[i].cOut();
if (iCanDrump > temp.buckets[j].cIn()) {
iCanDrump = temp.buckets[j].cIn();
}


// 开始倒油
temp.buckets[i].out(iCanDrump);
temp.buckets[j].in(iCanDrump);


// 判斷MySet中是否存在


if (caseSet.contan(temp.toString())) {
// 倒回去
temp.buckets[j].out(iCanDrump);
temp.buckets[i].in(iCanDrump);
continue;
}


// 再備份一個,來搜索
// DrumCase v = new DrumCase(temp);
// v.parent = u;
caseSet.add(temp.toString()); // (3)
temp.parent = u; // 记录父结点
dfs(temp, caseSet);
// 还原现场
temp.buckets[i].in(iCanDrump);
temp.buckets[j].out(iCanDrump);


}


}


}


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


最后是一个广搜算法BFS,其实代码区别都不大,只要懂得原理都很容易理解的:



import cn.hncu.search.oil.common.Bucket;
import cn.hncu.search.oil.common.DumpCase;
import cn.hncu.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;
}
}

}



写完这个代码后,感觉对深搜和广搜都有了些进步,以及对象的深拷贝捆绑等问题,也可以更清晰的了解了~~希望对大家有帮助















0 0
原创粉丝点击