园子里有朋友提到这个问题,相信各位都见过这种题,有时面试也会碰到。
解题方法有很多,有人靠猜、穷举、倒推,在倒水次数很多的情况下,就会比较麻烦了。
早些年的时候我得出了一种通用而简单的解此类题目的方法。
以一个5升一个6升杯子倒3升水为例,这两个杯子根据装的水量不同,共有22种状态,而加水倒水的过程就是在这些状态之间切换的过程,两个杯子都是空的是初始状态,其中有一个杯子的水是3升是最终状态,所以这个问题可以用有限状态机来解决。为了直观起见,把这些状态根据两个杯子的水量分布在二维表格中,于是乎得出了下面这个图:
图1
图中:横坐标表示一个杯子(A)的水量,纵坐标表示另一个杯子(B)的水量,灰色框表示不存在的状态,黄色框表示初始状态,蓝色框表示最终状态。
下面就是如何在这些状态中进行切换了。首先,可以把某个杯子加满水,在图中则表示为从零状态越过灰色区域跳到对面的满状态,把B杯子加满就是从上下到,如图2,把A杯子加满就是从左到右,如图3。
图2 图3
把某个杯子倒空,则方向相反,图略。
将水从某个杯子倒进另一个杯子则可以用从原状态向左下或右上方向的45度线表示,目标状态就表示倒入的量和原杯剩余的量。如图4表示将盛有4L水的A杯子中的水倒入B杯,倒完后B杯水有4L,A怀为空。图5表示A杯满,B杯1L,从A杯倒入B杯后,B杯满,A杯剩2L。图6表示A杯1L,B杯满,将B杯倒入A杯后,A杯满,B杯空。这些都是允许的状态变化。变化的原则就是只能在左下倒右上的45度方向上,因为在这个方向上的变化,水总量是不变的。
图4 图5 图6
现在,得出了几种允许的变化,看能不能从初始状态沿着这样的线到达最终状态。
一开始,就有两种选择,先试试向右吧。得到图7。
图7
接下来只有两种选择,向左下或者向下,向下就是都倒满,接下来的操作只能再倒空一个杯子,没什么意义,所以只有一种选择,向左下,到(1,5),得到图8。
图8
接下来有两种选择,显然,再回去以及向右都是没有意义的,所以只有一种选择,那就是向上到(1,0),得到图9。
图9
同样的,向右的目的地已经走过了,那么向左下,得图10。
图10
按这样的思路,最终可以得到图11。达到了最终状态。
图11
这里得出的解就是:
1、A加满(6,0)
2、A倒B(1,5)
3、B倒空(1,0)
4、A倒B(0,1)
5、A加满(6,1)
6、A倒B(2,5)
7、B倒空(2,0)
8、A倒B(0,2)
9、A加满(6,2)
10、A倒B(3,5)(得到3L)
如果一开始先向下也就是先加满B,结果也是比较类似的。
如果到达某个状态,找不到下一步,或者所有下一步都会回到以前曾经到达过的状态,则无解。
在两个杯子的情况下,除第一步外基本没有可多选的分支。
如果在多个杯子的情况下,则用多维距阵来表示状态,而可选分支也会随维度增加而增加,形成搜索树,各位可以自己进一步进行探索。