剑指offer_剪绳子
题目描述:
把一根绳子剪成多段,并且使得每段的长度乘积最大
n = 2 return 1 (2 = 1 + 1) n = 10 return 36 (10 = 3 + 3 + 4)
解法一:贪心法
尽可能多剪长度为3的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为3的绳子中拿 出一段与长度为1的绳子重新组合,把它们切成两段长度为2的绳子。
思想:
我们首先考虑分成哪些数时乘积才会尽可能的大。
首先不能分成数里不能有1
其次不能有大于4的数,否则可以将这个数再拆分成2和另一个数的和,这两个数的乘积一定比原数大。
再次因为,4=2*2,故我们只考虑将数分成2和3
证明:当 n >= 5 时,3(n - 3) - n = 2n - 9 > 0,且 2(n - 2) - n = n - 4 > 0。因此在 n >= 5 的情况下,将绳子剪成一段 为 2 或者 3,得到的乘积会更大。又因为 3(n - 3) - 2(n - 2) = n - 5 >= 0,所以剪成一段长度为 3 比长度为 2 得到的乘 积更大。
1 class Solution { 2 public int integerBreak(int n) { 3 if(n==2) return 1; 4 if(n==3) return 2; 5 int num3 = n/3; 6 if(n-num3*3==1) num3--; 7 int num2=(n-num3*3)/2; 8 return (int)(Math.pow(3,num3))*(int)(Math.pow(2,num2)); 9 } 10 }
解法2:动态规划法
①使用dp[i]表示正整数i的最大乘积,则
dp[i]=max{dp[i-1]*1,dp[i-2]*2,...,dp[i-(i-1)]*(i-1)
(i-1) * 1,(i-2) * 2,(i-3)*3,.....(i) * (i-1)};
1 class Solution { 2 public int integerBreak(int n) { 3 int[] dp = new int[n+1]; 4 dp[1] = 1; 5 for(int i=2;i<=n;i++){ 6 for(int j=1;j<=i-1;j++){ 7 dp[i] = Math.max(dp[i],Math.max(j*dp[i-j],j*(i-j))); 8 } 9 } 10 return dp[n]; 11 } 12 }
但事实并没有这么麻烦,因为这些正整数拆分最终总会拆分为2,3
因此调整状态转移方程为:dp[i]=max(dp[i-2]*2,dp[i-3]*3);
1 class Solution { 2 public int integerBreak(int n) { 3 if(n == 2) 4 return 1; 5 if(n == 3) 6 return 2; 7 int[] dp = new int[n+1]; 8 dp[0] = 0; 9 dp[1] = 1; 10 dp[2] = 1; 11 dp[3] = 2; 12 int p,q; 13 for(int i=4;i<=n;i++){ 14 p = Math.max(dp[i-2]*2,(i-2)*2); 15 q = Math.max(dp[i-3]*3,(i-3)*3); 16 dp[i] = Math.max(p,q); 17 } 18 return dp[n]; 19 } 20 }

浙公网安备 33010602011771号