广度优先搜索(BFS)解决倒水问题
背景
小时候看过一些智力测验之类的倒水问题,例如:
有两个杯子,体积分别为3和5,如何倒出体积为4的水。
建模
使用动态规划和广度优先搜索的思想对这个问题进行建模。记两个杯子的体积分别为 A , B A,B A,B,我们要倒出的目标记为 T T T。
状态: ( a , b ) (a,b) (a,b)表示两个杯子当前时刻各自有多少水。
转移方程:
-
( a , b ) → ( 0 , b ) (a,b)\rightarrow(0,b) (a,b)→(0,b):倒空A
-
( a , b ) → ( A , b ) (a,b)\rightarrow(A,b) (a,b)→(A,b):倒满A
-
( a , b ) → ( a , 0 ) (a,b)\rightarrow(a,0) (a,b)→(a,0):倒空B
-
( a , b ) → ( a , B ) (a,b)\rightarrow(a,B) (a,b)→(a,B):倒满B
-
{ a → M a x ( 0 , a + b − B ) b → M i n ( B , a + b ) \left\{ \begin{array}{ll} a\rightarrow Max(0, a+b-B) \\ b\rightarrow Min(B, a+b) \end{array}\right. {a→Max(0,a+b−B)b→Min(B,a+b) 从A倒出到B
-
{ a → M i n ( A , a + b ) b → M a x ( 0 , a + b − A ) \left\{ \begin{array}{ll} a\rightarrow Min(A, a+b) \\ b\rightarrow Max(0, a+b-A) \end{array}\right. {a→Min(A,a+b)b→Max(0,a+b−A) 从B倒出到A
求解思想:从原点 ( 0 , 0 ) (0,0) (0,0)出发,使用广度优先策略对整个空间进行搜索,直到 a = T a=T a=T或者 b = T b=T b=T。
代码实现
A, B = 3, 5
T = 4
# 从A倒出到B
def a_out(a, b):
x = max(0, a+b-B)
y = min(B, a+b)
return x, y
# 从B倒出到A
def b_out(a, b):
x = max(0, a+b-A)
y = min(A, a+b)
return y, x
# 记录状态转移方程
func_list = [
lambda a, b: (0, b),
lambda a, b: (A, b),
lambda a, b: (a, 0),
lambda a, b: (a, B),
a_out, b_out
]
# 记录当前状态的上一个状态,用于回溯
post_action = [[None for _ in range(B+1)] for _ in range(A+1)]
# 记录当前状态是否被遍历过,可以和上面的post_action优化成一个二维数组
vis_map = [[False for _ in range(B+1)] for _ in range(A+1)]
# 待遍历的状态列表
tasks = [(0, 0)]
def solve():
while len(tasks) > 0:
a, b = tasks.pop(0)
vis_map[a][b] = True
if b == T or a == T:
return a, b
# 状态(a, b)的所有可达状态列表
temp_task = [f(a, b) for f in func_list]
for x, y in temp_task:
if vis_map[x][y]:
continue
post_action[x][y] = (a, b)
tasks.append((x, y))
return 0, 0
# 根据post_action进行回溯
def back(target):
a, b = target
if post_action[a][b] is None:
print(str((a, b)))
return
print(str((a, b)))
back(post_action[a][b])
res = solve()
back(res)
结果
(3, 4)
(2, 5)
(2, 0)
(0, 2)
(3, 2)
(0, 5)
(0, 0)
从下往上看:
- 先将B杯倒满
- 再从B杯中倒出3给A杯,B剩余2
- 清空A杯
- 将B杯中的2倒给A杯,此时A杯有1的空余
- 再次将B倒满
- 向A倒出1,最终B杯剩余4
思考
广度优先搜索能保证找出来的路径为最短路径。
改变程序参数后观察发现,貌似任意两个互质的整数,都可以作为杯子体积构造出任意一个小于它们最大值的数。暂时没找到证明过程。