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