鱼C论坛

 找回密码
 立即注册
查看: 2114|回复: 4

[技术交流] Python思考练习题2_三个水桶等分8升水问题(已公开答案)

[复制链接]
发表于 2018-9-29 11:09:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 大头目 于 2018-10-9 09:06 编辑

玩法申明(借鉴每日一题):

1. 楼主看情况提供答案(有可能是自己做不出,但觉得很好的题目)
2. 请大家先独立思考,再参考其他鱼油的解答,这样才有助于自己编程水平的提高。开始阶段是看不到其他人的回帖的,等答题完成,过几天后取消限制。
3. 鼓励大家积极答题,但楼主囊肿羞涩大部分情况无法奖励大家,除非遇到楼主非常想知道答案的题目或者代码写得很好且有说明注释的答案。
4. 纯属娱乐,大家如果希望知道题目出处的可以私聊我,互相推荐好书,交流看书心得。

题目:
有三个容积分别是3升、5升、8升的水桶,其中容积为8升的水桶中装满了水,容积为3升和5升的水桶中是空的。
三个水桶都没有体积刻度,现在需要将水桶中的8升水等分成两份,每份都是4升水,条件是只能使用另外两个空桶,不能借用其他辅助容器。
求一共由多少种方法
PS:本题实现对于新手来说太困难了,三天后公布答案,供学习参考

附加要求:
需要记录倒水流程

提示:
题目是个经典的简单问题,人脑算很简单,通过倒水凑出1升水空间就行。但是用计算机求解的话就要利用经典的状态转移和穷举搜索算法。
1,不能向自身倒水
2,倒出水的桶不能为空桶
3,接收水的桶不能为满桶
4,避免进入死循环
Capture.PNG

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-10-8 10:45:15 | 显示全部楼层
import itertools 
state0 = [0,0,8]   #水桶的初始状态
state1=[0,4,4]   #水桶最终状态
maxv = [3,5,8]    #每个水桶的对应的容积
allowed=list(itertools.permutations(range(3),2))   #1-->2,2-->1,1-->3,3-->1,2-->3,3-->2共6种可能
def nextstate(state0):    #对于状态state0,推出所有可能的下一状态    
    for i,j in allowed:
        a,b=state0[i],state0[j]   # a-->b
        if a>0 and b<maxv[j]:
                nxtState = list(state0)
                if a + b > maxv[j]:     #部分倒入:a=a-(maxb-b)
                    nxtState[i] -= (maxv[j] - b)
                    nxtState[j] = maxv[j]
                else:          #a全部倒入b
                    nxtState[i] = 0
                    nxtState[j] = a+b
                yield nxtState

n=0
def findway(ways):        
        global n  #多少种方法        
        for state in nextstate(ways[-1]):        #遍历下一状态(ways[-1]表示最后一个状态)
                if state not in ways:         #当前状态未出现过。
                        ways.append(state)    #添加新结果
                        if state == state1:   
                                n+=1
                                print('第%d种,共%d步:' %(n,len(ways)),ways)    #输出结果
                        else:
                                findway(ways)  #递归搜索
                        ways.pop()   #去除当前循环中添加的状态(回归上一级,搜索另一支线。)

findway([state0])





第1种,共12步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第2种,共9步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第3种,共16步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [3, 5, 0], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第4种,共14步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第5种,共13步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [3, 5, 0], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第6种,共12步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [3, 5, 0], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第7种,共10步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第8种,共10步: [[0, 0, 8], [3, 0, 5], [3, 5, 0], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第9种,共13步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第10种,共8步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [0, 4, 4]]
第11种,共15步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第12种,共16步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 4, 1], [3, 5, 0], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第13种,共15步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [0, 2, 6], [2, 0, 6], [2, 5, 1], [3, 5, 0], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第14种,共11步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第15种,共12步: [[0, 0, 8], [0, 5, 3], [3, 2, 3], [3, 5, 0], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
第16种,共11步: [[0, 0, 8], [0, 5, 3], [3, 5, 0], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [1, 0, 7], [0, 1, 7], [3, 1, 4], [0, 4, 4]]
[Finished in 0.1s]
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-10-9 09:05:55 | 显示全部楼层
网上找到的高人的答案:
initial_bucket_state = [0,0,8]
#水桶的初始状态
bucket_volume = [3,5,8]
#每个水桶的对应的容积
from collections import deque
record = deque()
record.append(initial_bucket_state)
#利用python的deque队列记录状态转移情况,初始化时加入水桶初始状态。deque是可以从头尾插入和删除的队列,在不指定大小时,为一个无边界的队列


def nextStateLawful(current_state, bucket_volume):
    next_action = [
        (from_, to_) 
        for from_ in range(3) for to_ in range(3) 
        if from_ != to_ 
            and current_state[from_] > 0 
            and current_state[to_] < bucket_volume[to_] 
    ]
    #通过列表推导式获得下一动作的二元组构成的列表,由(倒出水的容器编号,倒入水的容器编号)组成。
    #二重循环得到下一步的所有可能动作,然后通过
    ##1.倒入倒出不能为同一个2.倒出的捅中必须有水3.倒入的桶中不能为满 的条件判断是否合法
    for from_, to_ in next_action:
        #next_state = current_state #浅复制造成错误
        next_state = list(current_state)
        if current_state[from_] + current_state[to_] > bucket_volume[to_]:
            next_state[from_] -= (bucket_volume[to_] - current_state[to_])
            next_state[to_] = bucket_volume[to_]
        else:
            next_state[from_] = 0
            next_state[to_] = current_state[to_] + current_state[from_]
        yield next_state
        #再由所有可能的合法动作得出所有的下一个状态,通过yield产生供其它函数调用。

num = 0
record_list = []
#记录调试的变量:num表示总共实现方法数,record_list记录所有实现路径

def searchResult(record, bucket_volume=[3,5,8], final_bucket_state=[0,4,4]):

    global num,record_list
    current_state = record[-1]
    #由record的末尾元素得到当前水桶状态
    next_state = nextStateLawful(current_state, bucket_volume)
    #得到关于当前状态的下一状态的可迭代生成器,供下一步循环使用
    for state in next_state:
    #遍历所有可能的下一状态
        if state not in record:
            #保证当前状态没在以前出现过。如果状态已经出现还进行搜索就会形成状态环路,陷入死循环。
            record.append(state)
            #添加新的状态到列表中
            if state == final_bucket_state:
                print(record)
                #打印出可行方案
                #record_list.append(record)这样使用错误,导致加入列表的是record的引用,应该使用下面的式子来进行深复制,得到一个新的队列再加入列表。
                record_list.append(deque(record))
                num += 1
            else:
                searchResult(record, bucket_volume, final_bucket_state)
                #不是最终状态则递归搜索
            record.pop()
            #去除当前循环中添加的状态,进入下一个循环,关键步,第一次实现的时候遗漏了

if __name__=='__main__':            
    searchResult(record)
    #开始
    print(num)
    #打印所有方案的数量
    print(min([len(i) for i in record_list]))
    #打印最短路径方案中的状态总数
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-10-9 14:54:20 | 显示全部楼层
grf1973 发表于 2018-10-8 10:45
第1种,共12步: [[0, 0, 8], [3, 0, 5], [0, 3, 5], [3, 3, 2], [1, 5, 2], [0, 5, 3], [3, 2 ...

厉害,佩服
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-10-10 09:38:56 | 显示全部楼层
我找的也是相同的高人。。。。。,自己改写了一下而已。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-23 07:47

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表