剪绳子问题

问题概述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m] 。请问 k[0]k[1]…*k[m] 可能的最大乘积是多少?

例如,当绳子的长度是8时, 我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

问题分析

数学推论

推论一: 将绳子 以相等的长度等分为多段 ,得到的乘积最大
推论二: 尽可能将绳子以长度 3 等分为多段时,乘积最大。

切分规则

最优:3。把绳子尽可能切为多个长度为 3的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
次优:2 。若最后一段绳子长度为 2;则保留,不再拆为 1+1。
最差:1 。若最后一段绳子长度为 1;则应把一份 3+1 替换为 2+2,因为 2×2>3×1。

算法步骤

当 n≤3时,按照规则应不切分,但由于题目要求必须剪成 m段,因此必须剪出一段长度为 1的绳子,即返回 n−1 。
当 n>3时,求 n除以 3的 整数部分 a和 余数部分 b。即 n=3^a+b,并分为以下三种情况:
当 b=0时,直接返回 3a3^a
当 b=1时,要将一个 1+3转换为 2+2,因此返回 3a13^{a-1}×4;
当 b=2时,返回$ 3^a$×2。

代码实现

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Cut_Rope {
public int getMaxMultiple(int n){
if(n<=3) return n-1;
int a=n/3; //这里之后就是n>3的情况
int b=n%3;
//在上述条件成立下继续判断b
if(b==0) return (int)Math.pow(3,a);
if(b==1) return (int)Math.pow(3,a-1)*4;
return (int)Math.pow(3,a)*2;
//这里即b不满足上述两种情况,默认b=2,不能再写判断了,
// 否则没有最终的返回
}
public static void main(String[] args){
Cut_Rope cut_rope=new Cut_Rope(); System.out.println(cut_rope.getMaxMultiple(10));
}
}

c++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution {

public:

int cuttingRope(int n) {

if(n<=3) return n-1;

int a=n/3; //这里之后就是n>3的情况

int b=n%3;
//在上述条件成立下继续判断b
if(b==0) return pow(3,a);
//c++下求幂直接用pow(a,b)
if(b==1) return pow(3,a-1)*4;

return pow(3,a)*2;



}

};

大数取模

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//基于贪心的思路
class Solution {
public int cuttingRope(int n) {
if(n == 2) {
return 1;
}
if(n == 3){
return 2;
}
int mod = (int)1e9 + 7;
long res = 1;
while(n > 4) {
res *= 3;
res %= mod;
n -= 3;
}
return (int)(res * n % mod);
}
}