21 Nov 2012

倒水问题的编程解决

收藏到CSDN网摘

这个题目的版本非常之多,有微软版的,腾讯版的,新浪版的等等,最常见的是:
有容量为a,b的2个水桶,需要得到c升的水.


UPDATE:
1.重新实现了方案b,要么a->b不停的倒,要么b->a不停的倒,直到目标出现或者水量重复(无解).
2.比较两种方案可以获得最佳方案.
3.根据checkio题目要求,输出倒水步骤:01表示填满a,12表示从a倒入b,20表示倒空b.(0,水源,1,第一个水桶,2,第二个水桶)
def checkio(a,b,c):
    print '------------------------------------------------'
    print '[A=%d]    [B=%d]    [C=%d] (target is C)' % (a,b,c)
    history = []    # pour history
    wlist = [(0,0)] # water of a,b
    # a->b repeadily
    while 1:
        # full a
        if wlist[-1][0]==0:
            wlist.append((a,wlist[-1][1]))
            history.append('01')
            if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
        # empty b
        if wlist[-1][1]==b:
            wlist.append((wlist[-1][0],0))
            history.append('20')
            if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
        # a->b
        if sum(wlist[-1])<=b:
            wlist.append((0,sum(wlist[-1])))
        else:
            wlist.append((sum(wlist[-1])-b,b))
        history.append('12')
        if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
    print 'A->B:',wlist
    list1 = [] # solution1
    hist1 = []
    if c in wlist[-1]: # found solution
        list1 = wlist[:]
        hist1 = history[:]
    else: # no solution
        list1 = []
        hist1 = []
        
    # exchange a,b
    history = []
    wlist = [(0,0)]
    # b->a repeadily
    while 1:
        # full b
        if wlist[-1][1]==0:
            wlist.append((wlist[-1][0],b))
            history.append('02')
            if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
        # empty a
        if wlist[-1][0]==a:
            wlist.append((0,wlist[-1][1]))
            history.append('10')
            if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
        # b->a
        if sum(wlist[-1])<=a:
            wlist.append((sum(wlist[-1]),0))
        else:
            wlist.append((a,sum(wlist[-1])-a))
        history.append('21')
        if c in wlist[-1] or wlist[-1] in wlist[:-1]: break
    print 'B->A:',wlist
    list2 = [] # solution2
    hist2 = []
    if c in wlist[-1]: # found solution
        list2 = wlist[:]
        hist2 = history[:]
    else:
        list2 = []
        hist2 = []
        
    # compare the solutions to have minimal steps
    result = []
    if not list1 and not list2:
        result = []
        print 'No solution'
        return result
    elif not list1:
        wlist = list2
        result = hist2
    elif not list2:
        wlist = list1
        result = hist1
    else:
        wlist = len(list1)<len(list2) and list1 or list2
        result = len(list1)<len(list2) and hist1 or hist2
    # print the result
    for item in wlist[1:]:
        print item[0],item[1]
    # return the result
    print 'Optimal solution:',wlist
    print 'Done'
    print '------------------------------------------------'
    return result

#These "asserts" using only for self-checking and not necessary for auto-testing
if __name__ == '__main__':
    print(checkio(5, 7, 6))  # ['02', '21', '10', '21', '02', '21', '10', '21', '02', '21']
    print(checkio(3, 4, 1))  # ["02", "21"]
    print(checkio(2, 1, 1))  # ["02"]
    print(checkio(2, 7, 1))
结果:
>>> ================================ RESTART ================================
>>> 
------------------------------------------------
[A=5] [B=7] [C=6] (target is C)
A->B: [(0, 0), (5, 0), (0, 5), (5, 5), (3, 7), (3, 0), (0, 3), (5, 3), (1, 7), (1, 0), (0, 1), (5, 1), (0, 6)]
B->A: [(0, 0), (0, 7), (5, 2), (0, 2), (2, 0), (2, 7), (5, 4), (0, 4), (4, 0), (4, 7), (5, 6)]
0 7
5 2
0 2
2 0
2 7
5 4
0 4
4 0
4 7
5 6
Optimal solution: [(0, 0), (0, 7), (5, 2), (0, 2), (2, 0), (2, 7), (5, 4), (0, 4), (4, 0), (4, 7), (5, 6)]
Done
------------------------------------------------
['02', '21', '10', '21', '02', '21', '10', '21', '02', '21']
------------------------------------------------
[A=3] [B=4] [C=1] (target is C)
A->B: [(0, 0), (3, 0), (0, 3), (3, 3), (2, 4), (2, 0), (0, 2), (3, 2), (1, 4)]
B->A: [(0, 0), (0, 4), (3, 1)]
0 4
3 1
Optimal solution: [(0, 0), (0, 4), (3, 1)]
Done
------------------------------------------------
['02', '21']
------------------------------------------------
[A=2] [B=1] [C=1] (target is C)
A->B: [(0, 0), (2, 0), (1, 1)]
B->A: [(0, 0), (0, 1)]
0 1
Optimal solution: [(0, 0), (0, 1)]
Done
------------------------------------------------
['02']
------------------------------------------------
[A=2] [B=7] [C=1] (target is C)
A->B: [(0, 0), (2, 0), (0, 2), (2, 2), (0, 4), (2, 4), (0, 6), (2, 6), (1, 7)]
B->A: [(0, 0), (0, 7), (2, 5), (0, 5), (2, 3), (0, 3), (2, 1)]
0 7
2 5
0 5
2 3
0 3
2 1
Optimal solution: [(0, 0), (0, 7), (2, 5), (0, 5), (2, 3), (0, 3), (2, 1)]
Done
------------------------------------------------
['02', '21', '10', '21', '10', '21']
>>> 


解法肯定有很多,可以用宽度优先搜索(BFS),也可以用穷举法。
这里列2个很有意思的解决方案,第一个很巧妙,可以手动解决,第二个则是终极(不一定是最优)解决办法,特别适合计算机完成.
# 解决思路:
# a,有限状态机 ,可以画图解决
# http://www.cnblogs.com/wisdomqq/archive/2009/09/22/1571520.html
# b,不断取余,不一定是最优解. 基本思想是用:用一个桶容量的倍数对另一个桶的容量取余。
# http://blog.csdn.net/morewindows/article/details/7481851


方案B的实现代码:
#! /usr/bin/env python
# coding=utf-8
 
# 倒水问题, 有容量为a,b的2个水桶,需要得到c升的水
# 解决思路:
# a,有限状态机 ,可以画图解决
#   http://www.cnblogs.com/wisdomqq/archive/2009/09/22/1571520.html
# b,不断取余,不一定是最优解
#   http://blog.csdn.net/morewindows/article/details/7481851
 
# 使用b方法,不停取余
def solve_2(a,b,c):
    wlist = [(0,0)] # 水量
    qlist = []      # 余数列表,如果不管怎么倒都循环,无解决方案
    t = a           # a的倍数
    while 1:
        # 装满A
        wlist.append((a,wlist[-1][1]))
        # A倒入B
        if sum(wlist[-1])<=b:
            wlist.append((0,wlist[-1][0]+wlist[-1][1]))
        else:
            wlist.append((sum(wlist[-1])-b,b))
        if t%b in qlist:
            break
        else:
            qlist.append(t%b)
     
        # 倒空B后A倒入B
        if wlist[-1][1]==b:
            wlist.append((wlist[-1][0],0))
            wlist.append((0,wlist[-1][0]))
        # 判断余数是否循环
        if t%b==c:
            break
        t += a
    # 无方案,交换a,b再来一次
    if c not in wlist[-1]:
        wlist = [(0,0)]
        qlist = []
        a,b = b,a
        t = a
        while 1:
            # 装满A
            wlist.append((a,wlist[-1][1]))
            # A倒入B
            if sum(wlist[-1])<=b:
                wlist.append((0,wlist[-1][0]+wlist[-1][1]))
            else:
                wlist.append((sum(wlist[-1])-b,b))
            if t%b in qlist:
                break
            else:
                qlist.append(t%b)
             
            # 倒空B后A倒入B
            if wlist[-1][1]==b:
                wlist.append((wlist[-1][0],0))
                wlist.append((0,wlist[-1][0]))
            # 判断余数是否循环
            if t%b==c:
                break
            t += a
        
        # 交换后还是无法解决
        if c not in wlist[-1]:
            print '[A=%d]    [B=%d]    [C=%d] (target is C)' % (b,a,c)
            print 'No possible solution'
        else:
            print '[A=%d]    [B=%d]    [C=%d] (target is C)' % (b,a,c)
            print '------------------------------------------------'
            print 'One possible solution is (not necessaryly to be optimal):'
            for item in wlist[1:]:
                if item[0]==a:
                    print '%8s    A : %d    B : %d' % ('Full B',item[1],item[0])
                elif item[0]<a and item[1]!=0:
                    print '%8s    A : %d    B : %d' % ('B -> A',item[1],item[0])
                elif item[1]==0:
                    print '%8s    A : %d    B : %d' % ('Empty A',item[1],item[0])
            print 'Done'
            print '------------------------------------------------'
    else:
        print '[A=%d]    [B=%d]    [C=%d] (target is C)' % (a,b,c)
        print '------------------------------------------------'
        print 'One possible solution is (not necessary to be optimal):'
        for item in wlist[1:]:
            if item[0]==a:
                print '%8s    A : %d    B : %d' % ('Full A',item[0],item[1])
            elif item[0]<a and item[1]!=0:
                print '%8s    A : %d    B : %d' % ('A -> B',item[0],item[1])
            elif item[1]==0:
                print '%8s    A : %d    B : %d' % ('Empty B',item[0],item[1])
        print 'Done'
        print '------------------------------------------------'

def main():
    solve_2(5,8,6)
 
if __name__ == '__main__':
    main()
测试结果:
>>> ================================ RESTART ================================
>>> 
[A=5] [B=8] [C=6] (target is C)
------------------------------------------------
One possible solution is (not necessary to be optimal):
Full A A : 5 B : 0
A -> B A : 0 B : 5
Full A A : 5 B : 5
A -> B A : 2 B : 8
Empty B A : 2 B : 0
A -> B A : 0 B : 2
Full A A : 5 B : 2
A -> B A : 0 B : 7
Full A A : 5 B : 7
A -> B A : 4 B : 8
Empty B A : 4 B : 0
A -> B A : 0 B : 4
Full A A : 5 B : 4
A -> B A : 1 B : 8
Empty B A : 1 B : 0
A -> B A : 0 B : 1
Full A A : 5 B : 1
A -> B A : 0 B : 6
Done
------------------------------------------------

No comments :

Post a Comment