待字闺中之最大乘积

原题

一根绳子,长度为n米。将其切成几段,每一段的长度都是整数。请给出一种切法,使得切成的各段绳子之间的乘积是最大的。注意,最少要切一下的。


【五分钟思考时间~~~独立思考~~~】

分析

这个题目如何一步一步的分析呢?不管切几段,总有第一段,第二段…等等。第一段的长度有哪些选择呢?可以是1、2、3...一直到n-1(至少要切一下),我们用max_prod(n)表示长度为n的绳子的切法中,乘积最大的值。那么:

  1. 当第一段长度为1时,最大的乘积为:max(1×max_prod(n-1), 1×(n-1))

  2. 当第一段长度为2时,最大的乘积为:max(2×max_prod(n-2), 2×(n-2))

  3. 当第一段长度为n-1时,最大的乘积为:(n-1)*1=n-1


上面为什么是取max呢?注意,题目中的要求,至少是要切一下的。这样,从上面的分析中得出,递归的表达式,设第一段绳子的长度为i,取值范围为[1,n-1],则,对于每一个i,有最大乘积为:max(i×(n-i),i×max_prod(n-i))。然后对所有的i,求的最大值,就是最终的答案。

这个题目到这里完了么?看过我们以前分析的同学,很快就能想到,进而查看,这些子问题中是否有重复的,如果有,则可以采用动态规划的方法进行算法改进。而查看是否有重复递归子问题,一个比较好的方法,就是在纸上画出递归树,然后是否有重复的递归子问题。就一目了然了。这个题目是比较明显的,同学们可以尝试自己在纸上画画,练习一下。

既然,我们已经明确了重复的递归子问题,然后呢?相信专门学习过动态规划专题的同学,都会记得这样的一句话:“自顶向下的分析问题,自底向上的解决问题”,大概类似,可能并不是原话。自底向上解决问题的意思就是先解决小问题,然后依据这些小问题的结果,再解决一批问题,依次直至解决整个问题:


  1. 当绳子的长度为1的时候,忽略,不能切

  2. 当绳子的长度为2的时候,切一刀,max_prod[2]=1×1

  3. 当绳子的长度为3的时候,切一刀,max_prod[3]=1×2

  4. 当绳子的长度为4的时候,依赖前面每一个长度的切法,并且,第一个长度从2开始。


这个,状态转移方程可以表示为:max_prod[i]=max((i-j)×j, j×max_prod[i-j]),其中j的范围是[1,i/2]。显然,动态规划方法的时间复杂度为O(n^2),空间复杂度为O(n)。

这个问题,到这里,已经挺不错的。不过,还没完,这个题目是有更加巧妙的方法的。在微博上,有的同学给出了如下的方法:只需要将长度n表示为3x+2y=n,并且3尽可能的多,这样的3^x+2^y是最大的。不得不赞叹,这确实是一个很巧妙的方法。大家可以通过例子,验证几个。为什么只有3和2呢?长度为4的,就是2×2,5以上的,都可以分解为3x+2y,并且3^x+2^y>5以上的数字。这个题目要求是整数,如果取消这个限制呢?拓展思路,举一反三,请大家多多思考。



我们来看程序是如何设计的:

我们先举一些具体的例子来分析这个问题。

假设绳子的长度大于2,因为大于2的绳子才能切割。

绳子的长度为2,我们进行一个切割,绳子的最大乘积是1*1

绳子的长度为3,我们可以进行二个切割1*1*1,一个切割1*2 我们选择1*2

绳子的长度为4,我们可以进行三个切割1*1*1*1,两个切割2*2,一个切割1*3,我们选择2*2

绳子的长度为5,我们可以进行四个切,1*1*1*1*1,三个切割1*1*1*2,两个切割1*2*2,一个切割2*3,我们选择2*3

根据上面进行的实际例子分析,我们有了一些具体的分析方法了,

用动态规划的思想来解决:

我们先来考虑一下初始化状态,由于要我们新建一个len+1的数组。

由于每根绳子都可以切割n-1端,而且这就使得绳子的最小长度为1.

根据上面的分析可以知道,动态规划的计算时从绳子大于等于3开始计算的。


dp[i] 表示长度i的最大乘积。 于是状态转移方程有max(max(dp[i-1],i-j)*j) 下面是代码的实现:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

int maxMult( int n)
{
    vector< int> dp(n +  11);
     int len, i;
     for (len =  3; len <= n; len++)
    {
         for (i =  1; i < len; i++)
        {
             int t = max(dp[len - i], len - i) * i;
             //dp[len-i]可能小于(len-i),因为dp[len-i]表示长度为len-i的分割乘积的最大值,
             //但len-i是没有分割的,两者不重合
            dp[len] = max(dp[len], t);
        }
    }
     return dp[n];
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值