三个水杯倒水问题
问题描述:
有3个水杯,水杯的容量为a,b,c升,a,b,c为正数,水杯可以从水源处接满水或将水倒入水源,也可以将杯子中的水倒入其他杯中,请问可以量出多少种体积的水,并列出。(注:量出的水必须在某一个杯中)
解答
错误想法
一开始,我想着用数学方法处理它,把倒水分为两种,一种是将满杯的水倒入另一杯中,如果另一个杯是空的,当前杯中的水相当于做减法,另一种是另一杯子是有水的,那么就相当于给另一杯中的水做加法。然后呢,我就想枚举出所有加法和减法的可能,很显然,这种方法会遗漏一些情况,因为从一杯水往另一个杯子倒水,两个杯子都有可能处于非空但是非满杯的情况。
然后呢,我想了很久。
正确的枚举倒水操作
我重新思考这个问题,想一想每次倒水的或者清空杯子的操作是有限的:
- 给a杯充满水,给b杯充满水,给c杯充满水三种操作;
- 如果a杯非空清空a杯,如果b杯非空清空b杯,如果c杯非空情况c杯三种操作;
- 如果a杯非空将a杯中的水倒入b杯中,或者将a杯中的水倒入c杯中,同理b杯非空、c杯非空共6中可能的操作。
这样,每一次最多可以有12种操作的可能,可以想象存在一个无限容量的杯子,这12种操作就可以理解成a,b,c三个杯子加无限容量杯子共四个杯子两两之间的倒入倒出操作(4*3)。
终止条件
每一步的倒出操作可以枚举,但是呢,这个操作可以无限进行下去,我们必须找到一个可以让程序终止的条件。
我们假设当前杯子中水的体积为(x, y, z)作为状态,每次操作,状态(x,y,z)都会转移到另一个状态,如果这个状态在之前已经被处理过了,那么就不在处理这个状态。
如果我们把每一个状态都看成图中的一个顶点,那么终止条件就是所有可能的状态都被访问了。
所以我们可以用图的遍历程序解决问题。
广度优先遍历
我们为了表达每次取出每一次水的操作步骤最少,我们用广度优先遍历。
时间复杂度
O(abc),总共最多有(a+1)(b+1)(c+1)种可能的状态,产生每一个状态需要倒水一次。
空间复杂度
O(abc), 保存已处理的状态需要一个哈希表,总共最多有(a+1)(b+1)(c+1)种可能的状态。
编码
from collections import deque
def bfs(volumns=(3, 5, 7), state=(0, 0, 0), visited=set(), task=[], actions=[], ans={
}):
visited.add(tuple(state))
for idx, cur_vol in enumerate(state):
if cur_vol not in ans:
ans[cur_vol] =