昨天写的那道题是数组中除了一个元素外其余元素的乘积,这道题自然就想到了把一个数分成两个的和,然后积就是这两个数的积,而这两个数中的每个数又可以分成两个数,所以可以用动态规划的方法,dp[i] = dp[j]*dp[i-j]。但是这样的话就会有重复,就是j和i-j后面会交换,而且这样会少一个i*1也就是说dp[j]也会少一个j*1,所以可以用dp[i] = j * dp[i-j],这样的话还漏了一种情况就是j*(i-j),因为dp[i-j]里面是没有(i-j)*1的,只有(i-j-1)*1,所以要把这种情况补上,也就是说当其中一个加数为j时的最大积是Math.max(j*(i-j), j*dp[i-j]),dp[i]的值是要把所有j都遍历完取其中的最大值。
class Solution {public int cuttingRope(int n) {int[] dp = new int[n+1];dp[0] =0;dp[1]=0;for(int i = 2;i<=n;i++){int max = 0;for(int j=1;j<i;j++){int tmp = Math.max(j*(i-j), j*dp[i-j]);max = Math.max(tmp,max);}dp[i] = max;}return dp[n];}
}
题解的动态的规划是把绳子全拆成2的段或者3的段,这样的乘积最大,而且是要更多的拆分出3,它的证明非常长,记结论就好了,我记的算法课上老师讲过的一个找出假币的题目,也是把所有币分成2堆或者3堆是能最快找出假币的,这是题解代码:
class Solution {public int cuttingRope(int n) {if (n <= 3) {return n - 1;}int[] dp = new int[n + 1];dp[2] = 1;for (int i = 3; i <= n; i++) {dp[i] = Math.max(Math.max(2 * (i - 2), 2 * dp[i - 2]), Math.max(3 * (i - 3), 3 * dp[i - 3]));}return dp[n];}
}
题解还有一种用高数解的,看完这个就知道为什么要分成2和3了。
通过求极限的方法求到驻点是e,但是只能取整数,所以取和e相邻的2和3,然后又算到3更好,但是不是每个数都能全分成3的,所以有时候也要分成2。
什么时候取2,取多少个2呢?
于是就有以下代码:
class Solution {public int cuttingRope(int n) {if (n <= 3) {return n - 1;}int quotient = n / 3;int remainder = n % 3;if (remainder == 0) {return (int) Math.pow(3, quotient);} else if (remainder == 1) {return (int) Math.pow(3, quotient - 1) * 4;} else {return (int) Math.pow(3, quotient) * 2;}}
}