剑指offer-剪绳子

JZ14 剪绳子

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

数据范围:2≤n≤60
进阶:空间复杂度 O(1) ,时间复杂度 O(n)

输入描述
输入一个数n,意义见题面。
返回值描述
输出答案。
示例1

输入:
8
返回值:
18
说明:
8 = 2 +3 +3 , 2*3*3=18 

示例2

输入:
2
返回值:
1
说明:
m>1,所以切成两段长度是1的绳子 

解题思路
本文提供六种解题思路:

  1. 数学法找规律,时间复杂度0(n),满足要求
  2. 暴力递归,时间复杂度0(n!),不满足要求
  3. 记忆性优化,时间复杂度0(n!),不满足要求
  4. 动态规划,时间复杂度0( n 2 n^2 n2),不满足要求
  5. 找规律,转换为数学问题,时间复杂度0(n),满足要求
  6. 对方法5(找规律)优化,时间复杂度0(1),满足要求

代码

import java.util.*;
public class Solution {
    public int cutRope(int target) {
        return method6(target);
    }
    //方法一:数学法找规律 0(n) √
    public int method1(int target){
        //对于和为target
        //n个数和为target,
        //              若target%n==0:每个数都为target/n
        //              若target%n!=0:target%n个数为target/n + 1,剩余为target/n
        int maxVal=Integer.MIN_VALUE;
        for(int n=2;n<=target;n++){
            if(target%n==0){
                int tmp=pow(target/n,n);
                if(tmp>maxVal) maxVal=tmp;
                else break;
                System.out.println(n+":"+tmp);
            }else{
                int tmp=pow(target/n+1,target%n)*pow(target/n,n-target%n);
                if(tmp>maxVal) maxVal=tmp;
                else break;
                //System.out.println(n+":"+tmp+"/"+(target/n+1)+"/"+target%n+"/"+target/n+"/"+(n-target%n));
            }
            //System.out.println(n+":"+maxVal);
        }
        return maxVal;
    }
    //a^b
    public int pow(int a,int b){
        int res=1;
        for(int i=1;i<=b;i++){
            res*=a;
        }
        return res;
    }
    //方法二:暴力递归 0(n!) × 时间复杂度过高
    public int method2(int target){
        if(target<4) return target-1;
        else return dfs(target);
    }
    public int dfs(int n){
        if(n<=4) return n;
        int cnt=-1;
        for(int i=1;i<n;i++){
            cnt=Math.max(cnt,i*dfs(n-i));
        }
        return cnt;
    }
    //方法三:记忆化搜索 0(n!)优化 × 时间复杂度过高
    public int method3(int target){
        if(target<4) return target-1;
        int[] mem=new int[target+1];//init is zero
        return dfs_mem(target,mem);
    }
    public int dfs_mem(int n,int[] mem){
        if(n<=4) return n;
        if(mem[n]!=0) return mem[n];
        int cnt=-1;
        for(int i=1;i<n;i++){
            cnt=Math.max(cnt,i*dfs_mem(n-i,mem));
        }
        return mem[n]=cnt;
    }
    //方法四:动态规划0(n^2) √
    public int method4(int target){
        if(target<4) return target-1;
        int[] dp=new int[target+1];
        dp[1]=1;dp[2]=2;dp[3]=3;dp[4]=4;
        for(int i=5;i<=target;i++){
            int cnt=-1;
            for(int j=1;j<i;j++){
                cnt=Math.max(cnt,j*dp[i-j]);
            }
            dp[i]=cnt;
        }
        return dp[target];
    }
    //方法五:找规律求解--数学问题:0(n)
    //对表达式y = (n/x)^x求导后发现
    //当x=e时,y取最大值,因此保证没根绳子长度在2和3,3为主
    public int method5(int target){
        if(target<4) return target-1;
        int res=1;
        while(target>4){
            target-=3;
            res*=3;
        }
        return res*target;
    }
    //方法六:对方法五优化:0(1)√
    public int method6(int target){
        if(target<4) return target-1;
        if(target%3==0){//全部剪成长度为3的段
            return (int)Math.pow(3,target/3);
        }else if(target%3==1){//一段长度为4,其余长度为3
            return 4*(int)Math.pow(3,(target-4)/3);
        }else{//一段长度为2,其余长度为3
            return 2*(int)Math.pow(3,target/3);
        }
    }
}

JZ83 剪绳子(进阶版)

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

由于答案过大,请对 998244353 取模。

数据范围:2 ≤ n ≤ 10^14

进阶:空间复杂度 O(1) , 时间复杂度 O(logn)

示例1

输入:
4
返回值:
4
说明:
拆分成 2 个长度为 2 的绳子,2 * 2 = 4
或者直接不剪,答案也是4。 

示例2

输入:
5
返回值:
6
说明:
剪成一个长度为 2 的绳子和一个长度为 3 的绳子,答案为2*3=6

示例3

输入:
874520
返回值:
908070737

设计思路
采用上面一题的方法六(时间复杂度0(1)),但是由于数据规模过大,幂次运算时,采取快速幂的形式,使得时间复杂度变为0(logn)
代码

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * @param number long长整型 
     * @return long长整型
     */
    long mod=998244353;
    public long cutRope (long target) {
        // write code here
       return method(target);
    }
    public long method(long target){
        if(target<4) return target-1;
        if(target%3==0){//全部剪成长度为3的段
            return pow(3,target/3)%mod;
        }else if(target%3==1){//一段长度为4,其余长度为3
            return 4*pow(3,(target-4)/3)%mod;
        }else{//一段长度为2,其余长度为3
            return 2*pow(3,target/3)%mod;
        }
    }
    public long pow(long a,long b){ //a^b 采用快速幂0(logn)
        long res=1;
        while(b!=0){
            if((b&1)==1){
                res=res*a%mod;
            }
            a=a*a%mod;
            b>>>=1;//无符号右移
        }
        return res%mod;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值