问题描述:
初等数学中的分油(酒/水)问题,在世界各地有多个版本:
- 韩信遇到两个路人争执不下,原因是两人有装满10斤的油篓和两个3斤、7斤的空油篓,无法平均分出两份,每份5斤油。韩信是如何解决这个难题的?
- 某人有12品脱美酒,想把一半赠人,但只有一个8品脱和一个5品脱的容器,问怎样才能把6品脱的酒倒入8品脱的容器中。
- 一只水桶可容12杓水,还有两只空桶,一只容量9杓,另一只5杓,怎样利用这两只空桶来把这大水桶中满盛的水分做两半?
- 有装有14千克酒的容器,另外有可装5千克和9千克酒的容器,要把酒平分,该如何办?
- 有一个装满油的8公升容器,另有一个5公升及3公升的空容器各一个,且三个容器都没有刻度,试将此8公升油分成4公升。
思路分析:
1. 均分原理:如果两个数互质,那么只用这两个数相加减,就可以求出所有自然数。
2. 分油(酒/水)问题可以抽象如下:假设有3个容器A、B、C,A装满液体,,
+
,
为偶数,
和
为互质数的倍数。要求利用3个容器将A中液体均分成两等份。
3. 倾倒顺序共有=6种,在这里我们只考虑往一个方向倾倒(单向闭环),因此共有两种方法:
方法一:从A到B、从B到C、从C到A,其中A到B不能倒空、C到A必倒空、B到C分倒空与不倒空两种情况。
方法二:从A到C、从C到B、从B到A,其中A到C不能倒空、B到A必倒空、C到B分倒空与不倒空两种情况。
4. 倾倒条件:满足上述倾倒顺序且倒完后不与已有倾倒状态重复。四种倾倒情况只需也只能满足其一,若都不满足,该方法无法均分。
注意:如遇小数情况可转换为整数来解决(比如4,2.5,1.5可转换为8,5,3)。
参考代码:
def divide(m, left, middle, right):
"""均分过程"""
state, n = [(left,0,0)], 0 #初始化二维列表、索引
#循环结束条件:左容器与中容器中装入均分后的液体(方法一)或者左容器与右容器中装入均分后的液体(方法二)
while (state[n][0] != left / 2 or state[n][1] != left / 2) and (state[n][0] != left / 2 or state[n][2] != left / 2):
#新一轮倾倒前两个容器中液体的叠加状态
i = state[n][0] + state[n][1]
j = state[n][1] + state[n][2]
k = state[n][0] + state[n][2]
#从左到中,左不倒空,中倒满
if i > middle and (i - middle, middle, state[n][2]) not in state:
state.append((i - middle, middle, state[n][2]))
#从中到右,中不倒空,右倒满
elif j > right and (state[n][0], j - right, right) not in state:
state.append((state[n][0], j - right, right))
#从中到右,中倒空
elif j <= right and (state[n][0], 0, j) not in state:
state.append((state[n][0], 0, j))
#从右到左,右倒空
elif (k, state[n][1], 0) not in state:
state.append((k, state[n][1], 0))
else: #如果未执行过上述四种情况,循环结束
print('\n采用方法%s,无法均分!' %m)
return
n += 1
print('\n采用方法%s,总共需要倾倒%d次,具体过程如下:\n' %(m, n))
print('容 积: %3d %3d %3d' %(left, middle, right))
for num, group in enumerate(state):
print('第%03d次:' %num, end = ' ')
for each in group:
print('%3d' %each, end = ' ')
print()
if __name__ == '__main__':
"""主程序"""
A, B, C = map(int, input("请按从大到小顺序输入三个容器容积(以空格分开):").split())
divide('一', A, B, C) #方法一
divide('二', A, C, B) #方法二
运行结果: