一个智商高的商人,总是会想办法实现利益最大化。现该商人手中有一根长度为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; }
到此,我们就解决了该钢条分段的问题。但这仅仅只是至顶而下的策略(我的理解就是从大规模到小规模),在下篇中会对该问题的至下而上的策略作以解析。