递归讨论(二)

一个智商高的商人,总是会想办法实现利益最大化。现该商人手中有一根长度为n的钢条,如果不作处理卖的话,不太好卖,另外售价不是非常高。但根据该商人的长期销售经验,发现顾客常购买以下几种长度的钢条:

长度 1 2 3 5 6 7
价格 1 3 5 8 12 13

现问:该商人该如何给该钢条分段,才能获得最大的收益呢?

Input:n

Out:max_value

根据算法导论中:

我们记方法p(int num)为长度为num的钢条的最大收益。记i为从长num的钢条下截下来的长度,同时记price[i-1]为长度为i的钢条价值。

下面我们看看算法具体思想:

首先我们得知道,即使它是最大收益,它还是由一段段长度组成的,而且这一段段长度就在上述长度列表中。

其次我们要知道,即然是最大收益max_value,则当去掉一段i之后,num-i长度的最佳收益必然是max_value-price[i];(这句有点绕,请仔细想想)

1、我们考虑几种情况:

若num已经为0了,那么最大收益就为0;

若num>7时,则i的可能取值是{1,2,3,5,6,7};

若num<7时,则i的可能取值是{1,?,?,num};

2、再考虑:

当前长度为num>7,截取长度为i时

截取长度 当前最大收益可能性
1 p[num-1]+price[0]
2 p[num-2]+price[1]
3 p[num-3]+price[2]
5 p[num-5]+price[3]
6 p[num-6]+price[4]
7 p[num-7]+price[5]

 

当前num长度的最大收益就在当前收益的可能之中。嘿嘿,我们取它们值的max不就行了。(最大收益有且仅有这几种可能,不相信的可以自己证明下)

说到这里,我不得不提一个问题:

对于一个很多阶的楼梯,我们上楼时,可能一次跨一步,可能一次跨两步,在快上完的时候,我们是不是要么剩一阶,要么剩两阶啊。可能有人说,我剩0阶,或者说剩3阶。。。的,这种人我只能说,自己面壁去!

同样的道理,对待钢条的事,最后终归剩上述可能的长度。

谁不想当个mvp呢!!!!!!所以当前收益每个可能值为了获得最佳殊荣,当然要把自己变的大大的,当然也不能毫无根据的变吧,从它的构成可能看出,它们的值都是根据变量p[num-i]而变化,所以它们只有把p(num-i)变的大大的来让自己最佳啊。嘿嘿。。。。所以p(num-i)要为num-i长度的最大收益,不然老子不要你,影响我获荣誉。相信到这里,我们也可以明白怎么递归的了,由p(num)问题转化到了p(num-i)的问题。

int get_max(int* price,int* len,int num)    //price指向价格数组,len指向长度数组,num为钢条当前长度
{
    int result=0;
    if(num <= 0)        //当num==0时的情况
        return 0;
    for(int i=1;i<=6;i++)        //for循环遍历的作用正是当前收益几种可能性的体现
    {
        if(num >= len[i-1])//此处的条件判断,为了防止num-len[i-1]不出现负值
        {
            result =  std::max(result,get_max(price,len,num-len[i-1])+price[i-1]);//选出当前收益中的最佳收益
        }
    }
    return result;
}

这样的算法,让我联想起了递归篇一中斐波那契数列,看看复杂度方面来说,是不是有点略类似呢。。。。

还真有点呀。。。。举个例子:

p(30)计算时,我们要计算p(29),p(28),p(27),p(25),p(24),p(23);然后在计算p(29)时,我们要计算p(28),p(27),p(26),p(24),p(23),p(22);依此类推下去,我了个去的,我们要重复计算的还真是巨量啊。。。。这又与递归一篇的有某些类似啊;

该怎么解决呢。。。。我们可以采取一种记忆机制,把这些已经计算过的值给它计入数组中啊,下次再调用它的时候,我们直接数组中找不就行了。这样就省时省力了,同时也速度擦擦上来了

int get_max(int* price,int* len,int num,int *p)//新增的这个指针p指向一个记录数组,下标为长度num-1,我们用来记录对应长度num下的最大收益
{
    int result=0;
    if(num>=1&&p[num-1]>=0)    //此处判断就起到从记录表中查值作用。
        return p[num-1];
    if(num == 0)//由标记A处的num-len[i]以及其上的if判断,可以看出下次调用get_max时,可能出现num为0的情况
        return 0;
    for(int i=1;i<=6;i++)
    {
        if(num >=len[ i-1])
        {
            result =  std::max(result,get_max(price,len,num-len[i-1],p)+price[i-1]);//标记A处
        }
    }
    p[num-1] = result;
    return result;
}

到此,我们就解决了该钢条分段的问题。但这仅仅只是至顶而下的策略(我的理解就是从大规模到小规模),在下篇中会对该问题的至下而上的策略作以解析。

posted @ 2014-10-30 09:40  天地由我欣  阅读(279)  评论(0编辑  收藏  举报