题目
给你一根长度为 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)
解析
其实这应该算作一个数学问题,因为题目考察的能力和算法没有什么很大关联,还是考察基础数学的内容较多。
核心思想其实有两条:
1. 和一定,差小积大。也就是我们需要寻找到尽量多的、相等或相差为1的数,这样的乘积最大;
2. 3+3=2+2+2;3*3 > 2*2*2。
核心思想中其实不难发现,如果要寻找尽量多的数,则作为因子数的数就要尽量小,如2,3,4,5等等,又因为4可以分解为2+2=2*2,所以4的本质就是2;5=2+3,可以拆解为更小的单位,且2*3>5,因此我们将目标锁定在 2 和 3 上。
究竟要选2还是3呢?根据核心思想2,可以发现和相同的情况下,用3的乘积比2的要大,因此我们在分解和数的时候要尽量用3分解。
任意一个分解数number只会服从下列一种情况:
1. number是3的倍数,那么乘积自然而然就是将number中的所有3相乘即可;
2. number除3余1,这时候需要考虑将分解出来的最后一个3另作考虑,即3*1与2*2哪个更大, 显而易见是后者。因此number除3余1时,需要将最后一个3拿出来和1配成2个2另作考虑;
3. number除3余2,乘积则为所有的3相乘后再乘2;
代码
int cutRope(int number ) {
int iThreeNum = 0; //分解数中包含3的个数
int iTwoNum = 0; //分解数种包含2的个数
if (3 >= number) //如果绳长为2或3,只能分解为1*(number-1)的形式
return number - 1; //返回乘积
iThreeNum = number / 3; //为了有尽量多的3,则先将number中的3减干净
if (1 == (number % 3)) //如果number除3余1
{
iThreeNum--; //则将3的个数-1,将3+1变为2+2
}
iTwoNum = (number - 3 * iThreeNum) / 2; //减掉所有3之后,剩下的就只有2或1,1可以忽略,所以直接除2就是2的个数
return (int)(pow(3, iThreeNum) * pow(2, iTwoNum)); //利用乘方计算最终的乘积
}