剑指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 }

 

posted @ 2019-08-21 10:54  chyblogs  阅读(138)  评论(0)    收藏  举报