python算法题——搜索篇 Poj3414

文章描述了一个编程问题,要求通过两个容量分别为A和B的锅,使用fill,drop和pour操作,找到生成C升水的最短操作序列。解决方案是使用广度优先搜索(BFS),在每个状态下尝试所有可能的操作,并在找到目标状态时输出操作序列。代码中定义了状态类pots,用action数组记录每一步操作,通过BFS遍历状态空间,直到找到满足条件的序列。

描述

给你两个锅,体积分别为 A 升和 B 升。可以执行以下操作:

  1. fill(i) fill the pot i (1 ≤ i ≤ 2) from the tap;

  2. drop(i) empty the pot i to the drain;

  3. pour(i, j) pour from pot i to pot j; after this operation either the pot j is full (and there may be some water left in the pot i), or the pot i is empty (and all its contents have been moved to the pot j).

    意思就是到一次全部倒完,要么i空要么j空

编写一个程序来查找这些操作的最短可能序列,这些操作将在其中一个锅中产生恰好 C 升水。

输入

第一行也是唯一的一行是数字 ABC。这些都是从 1 到 100 和 C ≤ max(AB) 范围内的整数。

输出

输出的第一行必须包含运算序列 K 的长度。以下 K 行必须分别描述一个操作。如果有多个最小长度的序列,则输出其中任何一个。如果无法实现所需的结果,则文件的第一行和唯一一行必须包含单词“不可能”。

示例输入

3 5 4

示例输出

6
FILL(2)
POUR(2,1)
DROP(1)
POUR(2,1)
FILL(2)
POUR(2,1)

又是输出最小的值

那么就要用BFS(一般来说DFS是求总共的数目用)

因为有的操作都需要两个锅同时进行,也就是需要记录两个状态,因此定义一个结构体

class pots:
    def __init__(self, p1, p2, action):
        self.p1 = p1
        self.p2 = p2
        self.action = action

原来在这里用了index用于记录步数(说一下为什么想到用index来记录步数,因为可以新建一个step数组,需要给很大的初始长度,然后对应的进队列元素会有一个索引,用step[该索引] = step[上一个索引] + 1可以表示这里的步长)

better

那么这里为什么可以不用索引呢,因为有一种更好的办法,就是直接记录他的每一步操作,用一个action数组存储(但是需要deepcopy)因为直接将end.action = head.action会是浅复制,会出大问题。最后用了几步就是len(end.action)

BFS

def BFS(a, b, c):
    import queue
    q = queue.Queue()
    node = pots(0, 0, [])
    q.put(node)
    indexes = [0]
    while not q.empty():
        head = q.get()
        for i in range(6):  # 6种状态,第一种是填满i,第二种填满j,依次类推
            end = pots(head.p1, head.p2, [])
            end.action = copy.deepcopy(head.action)
            if i == 0:
                if end.p1 == a:#p1为满的时候不能fill
                    continue
                end.p1 = a
            elif i == 1:
                if end.p2 == b:#p2为满的时候不能fill
                    continue
                end.p2 = b
            elif i == 2:  # 倒水
                if end.p1 == 0:#p1为空不能倒水
                    continue
                end.p1 = 0
            elif i == 3:
                if end.p2 == 0:#p2为空不能倒水
                    continue
                end.p2 = 0
            elif i == 4:  # 第五种就是将i的水导入j中:有几种情况要舍去
                if end.p1 == 0:  # i中没有水不能倒
                    continue
                if end.p2 == b:  # j中是满的不能倒
                    continue
                jleft = b - end.p2
                if jleft >= end.p1:
                    end.p2 = end.p1 + end.p2
                    end.p1 = 0
                else:
                    end.p1 = end.p1 - jleft
                    end.p2 = b
            elif i == 5:
                if end.p1 == a:
                    continue
                if end.p2 == 0:
                    continue
                ileft = a - end.p1
                if ileft >= end.p2:
                    end.p1 = end.p1 + end.p2
                    end.p2 = 0
                else:
                    end.p2 = end.p2 - ileft
                    end.p1 = a
            end.action.append(i)
            if end.p1 == c or end.p2 == c:
                print(len(end.action))
                for i in range(len(end.action)):
                    k = end.action[i]
                    if k == 0:
                        print("FILL(1)")
                    elif k == 1:
                        print("FILL(2)")
                    elif k == 2:
                        print("DROP(1)")
                    elif k == 3:
                        print("DROP(2)")
                    elif k == 4:
                        print("POUR(1,2)")
                    elif k == 5:
                        print("POUR(2,1)")
                return
            # print(i)
            newnode = pots(end.p1, end.p2, end.action)
            newnode.action = copy.deepcopy(end.action)
            q.put(newnode)
            

这里对于index索引的变化需要格外注意,因为每次进入一个for,都只加了1到6的值

这样当下一层遍历的时候第一个加1就只是到了原来那个时候加2

因此我们创建一个indexes用于记录这个index有没有放过

indexes = [0, 50000] #只放两个初始的值

但是这样也会浪费很多时间,有什么好方法可以辨别不同的方案呢(除了index)

当然有,就是用action记录每次的步骤,记得用deepcopy

主体

if __name__ == '__main__':
    a, b, c = map(int, input().split())
    BFS(a, b, c)

完整代码就在BFS中,就不展示了

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值