剪绳子(剑指offer 14-1题)

  • 这道题我拿到之后觉得第一个比较麻烦的点就是分成多少段是不确定的,处理起来就比较抽象,于是自然联想到分段数处理,于是我构建了一个函数getMax(int n, int i),它用来求长为n的绳子分成i段的最大积,然后在调用处循环每一个可能的i,取这些最大积中的最大值。
  • 于是有如下代码: 
class Solution {
public:
    int cuttingRope(int n) {
        int maxProduct = 1,temp = 0;   
        for(int i=2; i<=n; i++){ //剪成2段到剪成n端取最大
            temp = getMax(n,i); //长度为n的绳子剪成i端的最大积
            if(temp>maxProduct)
                maxProduct = temp;
        }
        return maxProduct;
    }
    
    int getMax(const int& n, const int& i){
        if(i == 1)
            return n;
        int maxProduct = 1,temp=0;
        for(int j=1; j<=n-i+1; j++){ //分出一段长度为j,然后再剪剩下的
            temp = j * getMax(n-j,i-1);
            if(temp>maxProduct){
                maxProduct = temp;
            }
        }
        return maxProduct;
    }
};

OK测试样例也没问题,提交,叮!超出时间限制。

分析知我们这个算法时间复杂度是O(n)级别的,而且包含了大量的重复计算,为什么这么说呢?

例如计算:getMax(5,2)

他会计算1 * getMax(4,1), 2 * getMax(3,1),3 * getMax(2,1),4 * getMax(1,1)

计算getMax(5,3)

他会计算1 * getMax(4,2),2 * getMax(3,2),3 * getMax(2,2)

其中每一个子项还要再次计算他的底层项

如getMax(4,2)还要再次计算1 * getMax(3,1)、2getMax(2,1)、3getMax(1,1)

所以整个递归算法包含了大量重复的计算,据此我们做出如下优化:

我们构建一个(n+1) * (n+1)的矩阵rememberMax来保存每一对对应n、i的getMax值,如getMax(n,i)的值存在矩阵的[n][i]位置(下标与值直接对应,废弃坐标为0的行列),其实我们还可以进一步探索这个矩阵

1、可以知道这是一个下三角矩阵,因为n<i的时候就不够分了(题目要求每段为整数),所以矩阵的上三角都为空

2、矩阵的第一列的值等于它们的行号,因为getMax(n,1) = n,只分成一段那最大积就是他自身

3、矩阵的对角线(左上到右下)元素的值都为1,因为getMax(n,n) = 1,把长为n的绳子分成n端那最大积就是111*...就是1

例如当n为4的时候我们会构建如下矩阵:

每次计算getMax时,如果这个值已经存在rememberMax矩阵中则直接返回,否则正常递归计算,然后把结果存在rememberMax矩阵中以备后用。

此算法时间复杂度仍为O(n2),但运算效率良好,执行用时beat 100%;但空间开销也比较大,因为采用了n*n的矩阵存储,空间复杂度为O(n2)

程序代码如下:

class Solution {
public:
    int cuttingRope(int n) {
        int maxProduct = 1,temp = 0; 
       // int (*rememberMAX)[60] = new int[n+1][60]();
        vector<vector<int> > rememberMAX(n+1,vector<int>(n+1,0));
        
        for(int i=1; i<=n; i++){  //初始化第一列
            rememberMAX[i][1] = i;
            rememberMAX[i][i] = 1;     //对角线为1
        }
        for(int i=2; i<=n; i++){ //剪成2段到剪成n端取最大
            temp = getMax(n,i,rememberMAX); //长度为n的绳子剪成i端的最大积
            if(temp>maxProduct)
                maxProduct = temp;
        }
        return maxProduct;
    }
    
    int getMax(const int& n, const int& i,vector<vector<int> >& rememberMAX){
        if(i == 1)
            return n;
        if(rememberMAX[n][i]!=0)
            return rememberMAX[n][i];
        int maxProduct = 0,temp=0;
        for(int j=1; j<=n-i+1; j++){ //分出一段长度为j,然后再剪剩下的
            temp = j * getMax(n-j,i-1,rememberMAX);
            if(temp>maxProduct){
                maxProduct = temp;
            }
        }
        rememberMAX[n][i] = maxProduct;
        return maxProduct;
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值