【剑指Offer记录】14_剪绳子

Part1. 我的思路和代码

把绳子剪成若干段后,这若干段各自又可以剪成若干段,如此循环,属于层层大问题包含小问题、小问题的最优解和大问题的最优解相关的情形,我想到了动态规划的方式:定义一个数组dp,假设绳子长度为i时的最大乘积已经计算好并保存到dp[i],那么当绳子长度大于i时,剪出的若干段如果包括长度为i的段,就可以利用dp[i]计算,免去了计算这段长度为i的绳子的最大乘积。

由题目得知绳子必须被剪为至少2段(长度分别为x、y),不能不剪。而这2段绳子各自或者不剪,或者剪了,当不剪时为最终的乘积提供的因数就分别是绳子长度本身x、y,当剪了时提供的因数分别是dp[x]、dp[y],要使最后乘积最大,因此计算x和dp[x]的最大值max_left、y和dp[y]的最大值max_right,将二者相乘即为乘积最大的结果。另外,将绳子剪成2段有多种方式,数学关系上为x+y=n,用一个循环遍历这些情况即可,x和y有不同的组合,因此采用自底向上的计算方式,绳子长度为1、2时最大乘积容易得到,从长度为3时开始计算直到长度为n。

该做法需要双循环,时间复杂度为O(n^2),题目提示还可以是O(n),但没有想到这种时间复杂度的做法,并且n最大值为60,所以用O(n^2)的方法也可以在1s的时间限制内完成计算

int cutRope(int n) {
    int ans;
    int dp[61] = {0};

    dp[1] = 1;
    dp[2] = 1;
    for(int len=3; len<=n; len++){
        for(int i=1; i<=len/2; i++){
            int right;
            int max_left;
            int max_right;

            right = len-i;
            max_left = i>dp[i]? i : dp[i];
            max_right = right>dp[right] ? right : dp[right];
            if(max_left*max_right > dp[len]){
                dp[len] = max_left*max_right;
            }
        }
    }
    ans = dp[n];

    return ans;
}

Part2. 其他做法

贪婪算法

是书中给出的方法,尽可能多剪出长度为3的绳子,假设剪出count段。此时,如果剪出count-1段时剩下的绳子长度为4,那么就不将这长度为4的绳子剪成长度为3、1的两部分,而是乘积更大的2、2两部分。这种做法是通过n>=5时,2(n-2)>n且3(n-3)>n且3(n-3)>=2(n-2)证明正确性的,但目前我不知道为什么能证明。按照此思路代码简洁许多,但我的写法和书上的有所不同,对“剩下的绳子长度不为4”的情况用了更多的if-else语句。

int cutRope(int n) {
    int ans;
    int count3 = 0;
    int left;

    if(n==2){
        ans = 1;
    }
    else  if(n==3){
        ans = 3;
    }
    else  if(n==4){
        ans = 2;
    }
    else{
        count3 = n / 3;
        left = n % 3;
        if(left == 0){
            ans = pow(3, count3);
        }
        else  if(left == 1){
            ans = pow(3, count3-1) * (2*2);
        }
        else{
            ans = pow(3, count3) * 2;
        }
    }

    return ans;
}

特别记录:这样感觉像是用公式解决一个题目,时间复杂度最大的部分或许就是调用pow()函数的步骤,该函数由于编译器的不同执行方式也不同,时间复杂度可能为O(n)或O(logn)。

Part3. 心得体会

  • 动态规划的题目非常考验思维
  • 对我个人而言是一种思维瓶颈,但数学知识的运用确实可以做出更高效的算法。
  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值