编程之美之买书问题

关于编程之美中买书的问题,我困扰了好久。当然,我的数学能力不强,领悟能力又差,所以才困扰了这么久。不过,在本人的坚持下,终于理解透彻。为了记住这一时刻,更为了免除以后忘记的可能,暂时记录如下。

书中关于证明F(y1-1,y2-1,y3-1,y4-1,y5)和F(y1-1,y2-1,y3-1,y4,y5-1)的等价性,作者举了一个例子,y1=y2=y3=y4=y5=2。但我认为,这个例子太过特殊,因为每卷本的数量都是相同的,显然只要调整后两项的顺序,(y1-1,y2-1,y3-1,y4-1,y5)和(y1-1,y2-1,y3-1,y4,y5-1)的序列便是完全一样的,这个是很好理解的,根本不需要再进行多余的解释。但是如果y4和y5不相等的话,就需要多解释一下了。我想,作者真是企图解释这种情况,但是却用了一个特殊的不用多做解释的例子。其次,我想,除了完全归纳法之外,通过举具体例子的方式去证明一个抽象的问题是不足的。那么应该如何证明呢?

其实,我也不知道怎么证明才好,只能根据自己的理解做一下逻辑推理,当然这个推理基于书中的推理但又做了延伸。让我们来看(y1-1,y2-1,y3-1,y4,y5-1)这个序列,由于y4>=y5, 所以y4>y5-1,那么在最后的最优解中,一定会出现只有y4卷而没有y5卷的一组(至少一组,可能多组,根据y4和y5的相对大小和最优解的分配情况)。那么把这个只有y4卷而没有y5卷的一组(只是一组,而不是多组,我想这一点书中作者没有说明清楚)中的y4卷换成y5卷后,得到的仍是同样的解,但是由于整体上看,y4卷少了一本,y5卷多了一本,这正是(y1-1,y2-1,y3-1,y4-1,y5)的序列。这时,可以回归书中的一句话(y1-1,y2-1,y3-1,y4,y5-1)的最优解必是(y1-1,y2-1,y3-1,y4-1,y5)的一个解,因此只要求出(y1-1,y2-1,y3-1,y4-1,y5)的最优解就好。

好了,对书中的这个内容作了补充解释之后,再来个声明:以上的推理也好,解释也好,都是以书中的解释为基础,加以引申的,也许不应该叫半原创吧。

上代码时间:

#!/usr/bin/env python

import random
def getMinPrice(books):
    books.sort()
    books.reverse()
    if books[0] <= 0:
        return 0,[]
    prices = []
    paths = []
    if books[4] >= 1:
        price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]-1])
        prices.append(5*8*(1-0.25)+price)
        paths.append(path)
    if books[3] >= 1:
        price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]])
        prices.append(4*8*(1-0.2)+price)
        paths.append(path)
    if books[2] >= 1:
        price,path = getMinPrice([books[0]-1,books[1]-1,books[2]-1,books[3],books[4]])
        prices.append(3*8*(1-0.1)+price)
        paths.append(path)
    if books[1] >= 1:
        price,path = getMinPrice([books[0]-1,books[1]-1,books[2],books[3],books[4]])
        prices.append(2*8*(1-0.05)+price)
        paths.append(path)
    if books[0] >= 1:
        price,path = getMinPrice([books[0]-1,books[1],books[2],books[3],books[4]])
        prices.append(8+price)
        paths.append(path)
    minPrice = min(prices)
    i = prices.index(minPrice)
    minPath = paths[i]
    minPath.insert(0,len(prices)-i)
    return minPrice,minPath

def getMinBookPrice(books):
    assert(type(books) == list)
    assert(len(books) == 5)
    for i in books:
        assert(type(i) == int)
    return getMinPrice(books)

def getMinBookPrice2(books):
    assert(type(books) == list)
    assert(len(books) == 5)
    for i in books:
        assert(type(i) == int)    
    path = []
    count5 = 0
    count3 = 0
    while books[0] > 0:
        books.sort()
        books.reverse()
        if books[4] >= 1:
            books = [books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]-1]
            path.append(5)
            count5 += 1
        elif books[3] >= 1:
            books = [books[0]-1,books[1]-1,books[2]-1,books[3]-1,books[4]]
            path.append(4)
        elif books[2] >= 1:
            books = [books[0]-1,books[1]-1,books[2]-1,books[3],books[4]]
            path.append(3)
            count3 += 1
        elif books[1] >= 1:
            books = [books[0]-1,books[1]-1,books[2],books[3],books[4]]
            path.append(2)
        else:
            books = [books[0]-1,books[1],books[2],books[3],books[4]]
            path.append(1)
        if count5 > 0 and count3 > 0:
            path.remove(5)
            path.remove(3)
            path.append(4)
            path.append(4)
            count5 -= 1
            count3 -= 1
    path.sort()
    path.reverse()
    price = 0
    for i in path:
        if i == 5:
            price += 5*8*(1-0.25)
        elif i ==4:
            price += 4*8*(1-0.2)
        elif i == 3:
            price += 3*8*(1-0.1)
        elif i == 2:
            price += 2*8*(1-0.05)
        else:
            price += 8
    return price,path

if __name__ == '__main__':
    books = [random.randint(1,7) for i in range(5)] 
    print getMinBookPrice(books)
    print getMinBookPrice2(books)

getMinBookPrice是归并法,getMinBookPrice2是贪婪法的修正实现,我发现测试了几次两个方法的结果是一致的。不过还是那句话,举例不足以证明贪婪法的修正实现的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值