PROBLEM

剑指 Offer 14- II. 剪绳子 II

难度 中等

MY ANSWER

使用快速幂求余,使用long long防止溢出。尽量将绳子切成以3为单位,若余1则将一段变4,若余2则乘上2这段。数学原理可看这篇题解,主要使用均值不等式、导数来证明。时间复杂度O(1),空间复杂度O(1)。

class Solution {
public:
long long fpow(long long x, long long y) {
long long res = x;
long long ans = 1;
while(y) {
if(y & 1) {
ans = ans * res % 1000000007;
}
res = res * res % 1000000007;
y = y>>1;
}
return ans;
}
int cuttingRope(int n) {
if(n == 2) {
return 1;
}
if(n == 3) {
return 2;
}
if(n == 4) {
return 4;
}
int res = n / 3;
int rem = n % 3;
if(rem == 0) {
return fpow(3, res);
}
if(rem == 1) {
long long tmp = fpow(3, res - 1) * 4 % 1000000007;
return tmp;
}
if(rem == 2) {
long long tmp = fpow(3, res) * 2 % 1000000007;
return tmp;
}
return 0;
}
};

BETTER SOLUTION

动态规划

创建数组 dp,其中 dp[i] 表示将正整数 i 拆分成至少两个正整数的和之后,这些正整数的最大乘积。状态转移方程为

image-20210905223215618

时间复杂度O(n^2),空间复杂度O(n)。

class Solution {
public:
int integerBreak(int n) {
vector <int> dp(n + 1);
for (int i = 2; i <= n; i++) {
int curMax = 0;
for (int j = 1; j < i; j++) {
curMax = max(curMax, max(j * (i - j), j * dp[i - j]));
}
dp[i] = curMax;
}
return dp[n];
}
};

状态方程还可根据和上面相似的数学原理优化为:

image-20210905223800839

时间复杂度O(n),空间复杂度O(n)。

class Solution {
public:
int integerBreak(int n) {
if (n < 4) {
return n - 1;
}
vector <int> dp(n + 1);
dp[2] = 1;
for (int i = 3; i <= n; i++) {
dp[i] = max(max(2 * (i - 2), 2 * dp[i - 2]), max(3 * (i - 3), 3 * dp[i - 3]));
}
return dp[n];
}
};

SUMMARY

记住该题面与数学结论,还有快速幂取余模板:

long long fpow(long long x, long long y) {
long long tmp = x;
long long res = 1;
while(y) {
if(y & 1) {
res = res * tmp % 1000000007;
}
tmp = tmp * tmp % 1000000007;
y = y>>1;
}
return res;
}
以x^13为例 初值 1101 110 11 1
res 1 x x x^5 x^13
tmp x x^2 x^4 x^8 x^16

即x^13 = x ^ 1 * x ^ 4 * x ^ 8。