题目:有三个容积分别是8升、5升和3升的水桶,其中容积为8升的水桶中有8升水,其它两个水桶是空的。三个水桶都没有刻度,问如何在不借助其它工具的情况下只使用这三个桶把8升水等分。
思路:把某一时刻三个水桶的水量称之为一个状态,则初始状态为{8, 0, 0}
,结束状态为{4, 4, 0}
。可以使用穷举法,从初始状态开始,根据状态变化的可能性(引起状态变化的操作)遍历所有可能的状态,每当找到一个从初始状态到最终状态的路径,即可认为找到了一个解。对于引起状态变化的操作,在本例中也就是倒水的动作,可以使用一个动作来建模,它包含三个要素:从哪个水桶倒水,倒进哪个水桶,以及倒了多少水。对于每个状态来说,总共有3x3种倒水的方式(包含一些不合理的倒水,比如向自己倒水),因此对于每个状态仅需要循环这3x3种可能性然后递归地从下个状态进行搜索即可,这在理论上来说是一个深度优先搜索。
剪枝:深度优先搜索可能会遇到重复状态引起的环路,比如上一个步骤从1向2倒了5升水,下一个步骤就从2向1倒了5升水,还可能有一些比较复杂的环路,所以需要把已经搜索过的状态添加到一个集合中去,在搜索一个状态前先看看是否已经搜索过,如果已经搜索过或者正在搜索则直接跳过即可。
代码:
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
public class SplitWaterTo2x4 {
Set<State> states = new HashSet<>(); //用于存储已经搜索过的状态的集合
Stack<State> order = new Stack<>(); //用于存储已经搜索过的状态的顺序,用于最后输出
Stack<Action> actions = new Stack<>(); //用于存储已经进行过的操作
//水桶的状态,分别对应8L、5L和3L的水桶
Bucket[] buckets = {
new Bucket(8), new Bucket(5), new Bucket(3)};
public void search(final State s) {
if (isFinalState(s)) {
printAction();
printOrder();
return;
}
states.add(s);
order.push(s);
//遍历所有可