zoukankan      html  css  js  c++  java
  • 递归讨论(二)

    一个智商高的商人,总是会想办法实现利益最大化。现该商人手中有一根长度为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;
    }

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

  • 相关阅读:
    C#实现二维码生成与解码
    js中正则表达式使用
    Busybox镜像
    linux删除文件后,空间未释放的一种情况,使用lsof查看
    linux中.nfsxxxx引起的文件无法删除
    linux中的查找命令find,locate,which,whereis
    openj9
    Ali流量控制中间件Sentinel
    LDAP认证模式简介
    nginx支持ipv6
  • 原文地址:https://www.cnblogs.com/qinghua0310/p/4061779.html
Copyright © 2011-2022 走看看