剪绳子问题
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m - 1] 。请问 k[0]k[1]…*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
提示:
2 <= n <= 1000
解题:动态规划
动态规划算法是通过拆分问题,定义问题状态和状态之间的关系,使得问题能够以递推(或者说分治)的方式去解决。
- 本题想要求的是不同情况下分绳子的乘积,假如我们要求长度为n的绳子拆分后的最大乘积,我们可以把它转化为长度为k和n-k长度绳子最大乘积相乘k*dp(n-k)的乘积。但是要注意,dp(n-k)是指n-k拆分为m段(m>1)时的最大长度,而如果这段绳子不拆分也有可能产生更大乘积。
- 得到表达式dp[n]=dp[k]*max(k,dp[n-k])
- k的取值从1开始,但是1不会扩大乘积,显然不能切出长度为1的绳子。
- k从2开始,一直到n/2都有可能产生更大的乘积,因为如果第一段长度大于n/2时,与小于n/2重复
class Solution {
public:
int cuttingRope(int n) {
vector<int> dp(n+1,1);
if(n==3) return 2;
for(int i=4;i<=n;i++){
for(int j=2;j<=i/2;j++){
dp[i]=max(dp[i],max((i-j)*j,dp[i-j]*j));
}
}
return dp[n];
}
};
贪心思路:
长为2的绳子,最大乘积应该是1*2,无拆分;
长为3的绳子,最大乘积是1*3,无拆分
长为4的绳子,最大乘积是1 * 4=2 * 2,无拆分和拆分结果相等
长为5的绳子,最大乘积是2 * 3=6,拆分结果更大
如何让绳子分得的乘积更大?每一小段乘积最大且小段数最多
长为6的绳子可以划分为3*3=9和2 *2 *2=8,划分成两段3更大
长为9的绳子可以划分为3*3 *3 =27和2 *2 *2 *2 *1=16
推测划分为最多的3,余数用1,2补充时乘积最大
如果题目增加一个限制条件
答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。
那么无法直接使用动态规划(取余数影响大小的比较),此时使用公式法更方便
class Solution {
public:
int cuttingRope(int n) {
return n<=3?n-1:(int)process(n);
}
long process(long n){
return n>4?(process(n-3)*3)%1000000007 : n;
}
};
n<=3情况比较特殊,n=1,n=2,n=3时结果分别为1,1,2(至少拆成两段)