美文网首页数据结构&算法&人工智能
剑指offer编程题—剪绳子

剑指offer编程题—剪绳子

作者: 零岁的我 | 来源:发表于2020-04-11 20:41 被阅读0次

题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

输入描述:
输入一个数n,意义见题面。(2 <= n <= 60)
输出描述:
输出答案。
示例
输入:8
输出:18

解题思路直接放在代码注释了。

/*题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],...,k[m]。
请问k[0]xk[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到
的最大乘积是18。
*/
#include <iostream>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;

//解题思路1:注意题目要求中的细节(m>1),即对于给定的绳子必须要剪成至少2段。
/*基本思想:这题感觉就是在找规律。用result数组记录对应长度的最大乘积,例如result[i]表示绳长为i时可能的最大乘积.
因为绳子剪裁后的段数必须大于1,所以绳长为2时,只能剪成长度为1的两段,所以result[2]=1*1,同理绳长为3时,只能剪成长度
为1和2的两段,因此result[3]=1*2.后面的依次类推,结果如下:
result[4]=2*2=4;
result[5]=2*3=6;
result[6]=3*3=9;
result[7]=2*2*3=12;
result[8]=2*3*3=18;
result[9]=3*3*3=27;
result[10]=2*2*3*3=36;
从上面的一系列推理可以看出,从result[7]开始后面的一系列都可以从前面的结果推出。
当n>=7时,result[i]=3*result[i-3];
*/
class Solution1 {
public:
    int cutRope(int number) {
        vector<int> result(number+6,0);
        result[1]=1;
        result[2]=1;
        result[3]=2;
        result[4]=4;
        result[5]=6;
        result[6]=9;
        for(int i=7;i<=number;++i){
            result[i]=3*result[i-3];
        }
        return result[number];
    }
};

//Solution1的优化方案
/*基本思想:从Solution1的推理过程中不难发现,k[0],k[1],...,k[m]中可能出现的数字只有2和3,也就是对于给定的绳长n,
当把绳子剪成x段长度为2的子段和y段长度为3的子段时,result[n]=result[2x+3y]=(2^x)*(3^y),要想乘积最大,要使长度为
3的子段尽量多,也就是y尽量大。
因此存在以下关系:对于给定绳长number,
y=number/3; 此时y最大
x=number%3; x=0 or 1 or 2;
x=0 说明子段的长度全部为3;
x=1 需要拿出一个长度为3的子段拼成长度为4的子段,也就是两个长度为2的子段
x=2 在剪出尽量多长度为3的子段后,最后剩余长度为2的子段,刚好。

其实这里对应一个数学定理,并且可以证明:
大数字都可以被拆分为多个2与3的和,以获取最大的乘积,只有2和3不需要拆分
网友证明链接:https://zhuanlan.zhihu.com/p/108832910
*/
class Solution2 {
public:
    int cutRope(int number) {
        if(number==2) return 1;
        if(number==3) return 2;
        int remainder=number%3; //余数
        int quotient=number/3; //商,也就是绳长可以剪成多少段长度为3的子段
        if(remainder==0) return pow(3,quotient);
        else if(remainder==1) return 4*pow(3,(quotient-1)); //余数为1则拿出一个3拼成4.
        else return 2*pow(3,quotient);
    }
};

//允许绳子的段数为1,即m>=1时的解决方案,这里与题目要求不同,可以存在不剪的情况,对应result[i]=i。
/*基本思想:自底向上的动态规划,动态规划算法的核心是记住已经已经求过的子问题的解。大概流程分三大步骤:
1.定义数组含义。这个数组用来记住已经求得的子问题解,在该题中,数组result记录对应绳长的最大乘积,
例如result[i]表示绳长为i,经过剪裁之后各子段的最大乘积,也就是说result[i]对应绳长为i时剪裁的最优解;
2.找出数组元素之间的关系式,也就是动态规划算法中常说的状态转换方程,在该题中,自顶向下分析有:对于
给定绳长n,将其分成长度为i和n-i的两段,则result[n]=result[i]*result[n-i],1<=i<n。 result[n-i]为
子问题的最优解,最终数组元素之间的关系为:result[n]=max{result[n],result[i]*result[n-i]},1<=i<n.
3.寻找初始值。有了数组元素之间的关系式,还得知道初始值才能自底向上求解。该题的初始值如下:
result[0]=0;
result[1]=1;
result[2]=2;
result[3]=3;
也就是绳长为1、2、3时,不剪对应的解为最优
剩下的可以由关系式推出,例如
result[4]=max{4,result[1]*result[3],result[2]*result[2]}
result[6]=max{6,result[1]*result[5],result[2]*result[4],result[3]*result[3],result[4]*result[2],result[5]*result[1]}
可以发现,上面有很多重复的计算,所以在利用子问题的最优解构建原问题的最优解时,只用遍历到i/2.
*/
class Solution3 {
public:
    int cutRope(int number) {
        vector<int> result(number+1,0);
        for(int i=1;i<=number;++i){
            result[i]=i;  //不剪
            //子循环中利用关系式和已求得的子问题的最优解来求解当前最优解,最终构造原问题的最优解
            for(int j=1;j<=i/2;++j){
                result[i]=max(result[i],result[j]*result[i-j]);
            }
        }
        return result[number];
    }
};
 //利用Solution2中的数学定理:一个大数可以拆分为多个2和3的和,以获取最大的乘积,只有2和3不需要拆分。
class Solution4 {
public:
    int cutRope(int number) {
        if(number==2) return 2;
        if(number==3) return 3;
        int remainder=number%3;
        int quotient=number/3;
        if(remainder==0) return pow(3,quotient);
        if(remainder==1) return 4*pow(3,(quotient-1));
        else return 2*pow(3,quotient);
    }
};
int main() 
{
    int n;
    cin>>n;
    Solution1 sol1;
    int res=sol1.cutRope(n);
    cout<<"Solution1:"<<res<<endl;

    Solution2 sol2;
    res=sol2.cutRope(n);
    cout<<"Solution2:"<<res<<endl;

    Solution3 sol3;
    res=sol3.cutRope(n);
    cout<<"Solution3:"<<res<<endl;

    Solution4 sol4;
    res=sol4.cutRope(n);
    cout<<"Solution4:"<<res<<endl;
    return 0;
}
运行结果

相关文章

  • 剑指offer编程题—剪绳子

    题目描述给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k...

  • 剑指offer - 剪绳子

    题目 给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k...

  • 剑指 Offer 第14题:剪绳子

    1、前言 2、思路 这道题用动态规划做。1、我们想要求 n 被剪掉后的最大面积,只需要求比 n 小的剪掉后的最大面...

  • 动态规划 && 贪婪算法

    动态规划 && 贪婪算法 1· 剪绳子(14 剑指offer ) 需要先从 base case 开始寻找规律 ,...

  • 全网最全剑指offer题目解答

    【剑指offer】Java版代码(完整版) 【剑指offer】1-10题 【剑指offer】11-20题 【剑指o...

  • 剑指 offer 学习之剪绳子

    题目描述 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子...

  • 剪绳子

    《剑指offer》面试题14:剪绳子 题目:给你一根长度为n的绳子,请把绳子剪成m段 (m和n都是整数,n>1并且...

  • 剑指offer编程题

    1,在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序...

  • 剑指Offer编程题

    说明: 本文中出现的所有算法题皆来自牛客网-剑指Offer在线编程题,在此只是作为转载和记录,用于本人学习使用,不...

  • 剑指offer第二版-14.剪绳子(动态规划)

    本系列导航:剑指offer(第二版)java实现导航帖 面试题14:剪绳子 题目要求:给你一根长度为n的绳子,请把...

网友评论

    本文标题:剑指offer编程题—剪绳子

    本文链接:https://www.haomeiwen.com/subject/fsrpmhtx.html