广度优先搜索(BFS)解决倒水问题

广度优先搜索(BFS)解决倒水问题

背景

小时候看过一些智力测验之类的倒水问题,例如:

有两个杯子,体积分别为3和5,如何倒出体积为4的水。

建模

使用动态规划和广度优先搜索的思想对这个问题进行建模。记两个杯子的体积分别为 A , B A,B AB,我们要倒出的目标记为 T T T

状态: ( a , b ) (a,b) (a,b)表示两个杯子当前时刻各自有多少水。

转移方程:

  1. ( a , b ) → ( 0 , b ) (a,b)\rightarrow(0,b) (a,b)(0,b):倒空A

  2. ( a , b ) → ( A , b ) (a,b)\rightarrow(A,b) (a,b)(A,b):倒满A

  3. ( a , b ) → ( a , 0 ) (a,b)\rightarrow(a,0) (a,b)(a,0):倒空B

  4. ( a , b ) → ( a , B ) (a,b)\rightarrow(a,B) (a,b)(a,B):倒满B

  5. { 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. {aMax(0,a+bB)bMin(B,a+b) 从A倒出到B

  6. { 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. {aMin(A,a+b)bMax(0,a+bA) 从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

思考

广度优先搜索能保证找出来的路径为最短路径。

改变程序参数后观察发现,貌似任意两个互质的整数,都可以作为杯子体积构造出任意一个小于它们最大值的数。暂时没找到证明过程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值