• 使用图的遍历解决分酒问题


    一个装满8升酒的瓶子,另外只有一个5升的空瓶子和一个3升的空瓶子,问怎样倒可以把酒一丝不差的分成两个4升?(当然不可以直接比较水平面,瓶子的底面半径不同)

    学习MIT的公开课计算机科学入门课程6.001x和6.002x将近一个学期了,我几乎把它当作第一重要的事情,每天早晨第一件事就是学习这门公开课。我确信我有很大的收获,正如他们的宗旨用计算思维解决真实问题using computation to solve real problem,我想计算思维是学习这门课最大的收获。
    最近看书,碰巧看到一个题目
    “一个装满8升酒的瓶子,另外只有一个5升的空瓶子和一个3升的空瓶子,问怎样倒可以把酒一丝不差的分成两个4升?”
    像是一道小学的奥数题,没准当年我遇到过,让我苦恼过。
    现在看来题目还是趣味十足,但也难度十足,如果真要我做,我想我肯定能做出来,因为我这么多年的学习让我感到,面对再困难的问题,去尝试就有可能得到解答。
    但现在嘛,我变得更不一样了,我可以用算法来解决这个问题。现在我知道这可以看成是一个图搜索问题,每一个节点是酒的一种状态,每一条边是倒酒所导致状态的切换。
    选择广度优先搜索,第一个给出的答案是操作次数最短的。图根据搜索过程动态产生节点。
    程序一眨眼就运行完了,没想到总共找到了16种结果,但每一种看起来都不太一样。最少的一种需要操作7次,quite FUN!

    实现程序的一个关键问题是,如何倒酒,现实情况是你知道哪个瓶子是满的,哪个瓶子还剩多少容量。往一个满的瓶子倒酒没有意义,从一个空瓶倒酒出来也没有意义。
    我的做法是,尝试从任意一个瓶子倒到另外一个瓶子里,只有6种可能。是否可以倒酒,由源酒瓶和目标酒瓶酒的容量决定。如果源酒瓶酒太多,目标酒瓶装不下,则目标酒瓶倒满;否则源酒瓶的酒太少,则把酒倒光。如果尝试去倒,各瓶酒的状态却未变,证明不可行,即停止继续尝试倒酒。

    Python代码

    volume = [8, 5, 3]
    
    class State(object):
        def __init__(self, one, two, three):
            self.v = [one, two, three]
            
        def transition(self, src, to):
            newV = self.v[:] #caution list is mutable
            # if src bottle don't have enough liqur, pour all
            if newV[src] < volume[to] - newV[to]:
                pour = newV[src]
            # if to bottle don't have enough space, fill it
            else:
                pour = volume[to] - newV[to]
            newV[src] -= pour
            newV[to] += pour
            return State(newV[0],newV[1],newV[2])
            
        def __eq__(self, other):
            return self.v == other.v
            
        def __str__(self):
            return "%s %s %s" % (self.v[0], self.v[1], self.v[2])
    
    
    def BFSWithGeneratorAll(start, end, q=[]):
        initPath = [start]
        q.append(initPath)
        paths = []
        while len(q) > 0:
            tmpPath = q.pop(0)
            lastNode = tmpPath[-1]
            if lastNode == end:
                paths.append(tmpPath)
            for (src, to) in zip([0,0,1,1,2,2],[1,2,0,2,0,1]):
                new = lastNode.transition(src,to)
                if new not in tmpPath:
                    newPath = tmpPath + [new]
                    q.append(newPath)
        return paths
        
    def printSolution(path):
        for elt in path:
            print elt
            
    start = State(8,0,0)
    end = State(4,4,0)
    
    paths = BFSWithGeneratorAll(start,end)
    for path in paths:
        printSolution(path)
        print


    找到的全部结果,每一行是酒的一种状态。起始状态是8L的壶里有8L酒,最终的状态是8L和5L的壶里各有4L酒。

    8L 5L 3L
    8 0 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    5 0 3
    0 5 3
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    3 5 0
    0 5 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    0 5 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    0 5 3
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    0 5 3
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    0 5 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    0 5 3
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    4 4 0
    
    8 0 0
    5 0 3
    5 3 0
    2 3 3
    2 5 1
    7 0 1
    7 1 0
    4 1 3
    0 5 3
    3 5 0
    3 2 3
    6 2 0
    6 0 2
    1 5 2
    1 4 3
    4 4 0
  • 相关阅读:
    生成随机串码并保存到Excel中
    制作100份word表
    抓取网页图片-以本地IIS网页为实践对象
    使用xlsxwriter 创建图表chart
    照片查看器2.0
    编程注意事项-记踩过的坑
    STC12C5A60S2的定时器模式16位的时候没有自动重载功能
    Keil C51 一个警告 '=': pointer: different mspace
    RT-Thread 使用笔记二
    Keil-C51读取ROM数据
  • 原文地址:https://www.cnblogs.com/meelo/p/4169562.html
Copyright © 2020-2023  润新知