zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【算法训练营day41】LeetCode343. 整数拆分 LeetCode96. 不同的二叉搜索树

2023-04-18 15:23:20 时间

LeetCode343. 整数拆分

题目链接:343. 整数拆分

独上高楼,望尽天涯路

一开始想到的是用数学证明化简的方法,每次拆成尽可能多的3,最后剩下的是2或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;
    }
};

慕然回首,灯火阑珊处

dp[i]表示的是分拆数字i,可以得到的最大乘积为dp[i]。

可以想 dp[i]最大乘积是怎么得到的呢?

其实可以从1遍历j,然后有两种渠道得到dp[i].

一个是j * (i - j) 直接相乘。

一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n + 1);
        dp[2] = 1;
        for (int i = 0; i < n + 1; i++) {
            for (int j = 1; j <= i / 2; j++) {
                dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));
            }
        }
        return dp[n];
    }
};

LeetCode96. 不同的二叉搜索树

题目链接:96. 不同的二叉搜索树

独上高楼,望尽天涯路

感觉是一道很难想的题,没有太好的思路。

慕然回首,灯火阑珊处

很巧妙的思路,需要举例并且画图才能推导出来。

首先,因为只需要返回不同二叉搜索树的种数,所以我们不太需要在意节点具体的值(感觉这是题目的障眼法),只需要知道它们是互不相同节点值的就行,顺着这条思路,我们就可以知道节点值为1,2,3组成的不同二叉搜索树的种数和节点值为43,154,6546组成的种数是一样的,这是因为我们只在乎相对大小!

其次,对于任意整数n,不同的二叉搜索树的数量都可以分解为:元素1为头节点搜索树的数量 + 元素2为头节点搜索树的数量 + ... + 元素n为头节点搜索树的数量。

元素1为头节点搜索树的数量 = 右子树有n - 1个元素的搜索树数量 * 左子树有1 - 1个元素的搜索树数量

元素2为头节点搜索树的数量 = 右子树有n - 2个元素的搜索树数量 * 左子树有2 - 1个元素的搜索树数量

...

元素n为头节点搜索树的数量 = 右子树有n - n个元素的搜索树数量 * 左子树有n - 1个元素的搜索树数量

因为我们只在乎相对大小,对于右子树有n - 1个元素的搜索树数量,我们不需要去细想有哪些节点值,我们只需要知道它们是互不相同的,这个数量和节点值从1到n - 1的搜索树数量其实是一样的!

所以,假设dp[i]表示的是由i个节点组成且节点值为1到i的互不相同的二叉搜索树的种数,上文中“右子树有n - 1个元素的搜索树数量”就可以表示为dp[i - 1],递推公式即为dp[i] += dp[i - j] * dp[j - 1]。

空节点也是一棵二叉搜索树,所以初始化dp[0] = 1。

class Solution {
public:
    int numTrees(int n) {
        vector<int> dp(n + 1);
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= i; j++) {
                dp[i] += dp[j - 1] * dp[i - j];
            }
        }
        return dp[n];
    }
};