343. 整数拆分
1.这道题自己没有做出来。本题dp数组及下标含义为:分拆数字i,可以得到的最大乘积为dp[i]。对于每一个i,当 i≥2 时,假设对正整数 i拆分出的第一个正整数是 j(1≤j<i),则有以下两种方案:将 i拆分成 j 和 i−j 的和,且 i−j 不再拆分成多个正整数,此时的乘积是 j×(i−j);将 i 拆分成 j和 i−j 的和,且 i−j 继续拆分成多个正整数,此时的乘积是 j×dp[i - j]。因此,当j固定时,dp[i] =j * max(i - j, dp[i - j])。j有多种取值,所以递推公式就变成了dp[i] = max(dp[i], j * max(i - j, dp[i - j]))。在如下代码中,我优化了j的取值范围。对于j*(i-j)拆成两个,前面一半和后面一半是对应的没有必要。对于j*dp[i - j],后面一半相当于把小的部分继续拆分不可能是最大值。所以j<=i/2。事实上j没必要>3,因为4和4以上的数字拆分之后的乘积都>=它们本身。
class Solution {
public:
int integerBreak(int n) {
vector<int> dp(n+1);
dp[2]=1;
for(int i=3;i<=n;i++){
for(int j=1;j<=i/2&&j<4;j++){
dp[i]=max(dp[i],j*max(i-j,dp[i-j]));
}
}
return dp[n];
}
};
2.数学法,本题也可以用贪心,每次拆成n个3,如果剩下是4,则保留4,然后相乘,但是这个结论需要数学证明其合理性!
class Solution {
public:
int integerBreak(int n) {
if (n == 2)
return 1;
if (n == 3)
return 2;
if (n == 4)
return 4;
int result = 1;
while (n > 4) {
result *= 3;
n -= 3;
}
result *= n;
return result;
}
};
3.动规优化版。事实上就是第一版j<4的那个优化思路,对于j>=3拆成1和其它部分必不可能是最大值,所以只需要考虑拆成2和其它部分或3和其它部分。(还是挺烧脑的,我个人也没完全弄明白,且随便看看吧)
class Solution {
public:
int integerBreak(int n) {
if (n <= 3) {
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];
}
};
96.不同的二叉搜索树
1.上来一看题目,好家伙这不是有一年408数据结构选择题吗?我当时还傻乎乎地一个一个找,看课后答案发现是卡特兰数。重拳出击,很快啊栈溢出了。
class Solution {
public:
int numTrees(int n) {
int a = 1, b = 1;
for (int i = 1; i <= n; i++)
a *= i;
for (int j = n + 1; j <= 2 * n; j++)
b *= j;
return b / a / (n + 1);
}
};
2.老老实实回忆当时的笨办法,枚举。左子树0个结点、左子树1个结点……左子树n-1个结点。秋豆麻袋,左右子树也是二叉搜索树。那么左右子树的二叉搜索树种类乘起来不就是当前二叉搜索树的种类吗,改变左右子树的结点个数,将总的结果加起来不就是二叉搜索树的所有种数吗。不难写出如下代码,其中dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]。(注意空节点dp[0]=1,表示空节点也是一棵二叉搜索树。)
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n + 1);
dp[0] = 1;
for (int j = 1; j <= n; j++) {
for (int i = 0; i <= j - 1; i++) {
dp[j] += (dp[i] * dp[j - 1 - i]);
}
}
return dp[n];
}
};
3.看了官方题解才发现,不是卡特兰数不能用,是我不会用。(逐步递推,这样就不会溢出了)
class Solution {
public:
int numTrees(int n) {
long long C = 1;
for (int i = 0; i < n; ++i) {
C = C * 2 * (2 * i + 1) / (i + 2);
}
return (int)C;
}
};
今日总结:整数拆分还是挺抽象的。