代码随想录算法训练营Day41|LC343 证书拆分&LC96 不同的二叉搜索树

一句话总结:初看觉得难,过几天再做依旧觉得不简单。

原题链接:343 整数拆分

拿到题乍一看两眼一抹黑。还是看题解吧。首先确定动规数组及下标的含义。这里就设置dp[i]为正整数i的最大乘积。

然后确定递推关系式。对于怎么求i的最大乘积,有两种方式,从1遍历到j,那么dp[i]有两种可能的取得途径:

  1. j 与 (i - j)直接相乘
  2. j与dp[i - j],相当于是递归地计算最大值。

因此dp[i]从j * (i - j), dp[i - j] * j, 以及dp[i]三者中取最大值即可。

第三步是对dp数组地初始化,这里初始化dp[0]和dp[1]从题目的角度来看是无意义的,因为拆分0和拆分1的最大乘积无意义。故直接初始化dp[2] = 1即可。

第四步是确定遍历顺序。通过递归公式dp[i] = max(dp[i], dp[i - j] * j, (i - j) * j)可以看出来,dp[i]依赖前面计算得到的dp[i - j],因此从前往后遍历。

通过以上四步已经可以写出来完整的代码了,但为了程序的完整性,还需要一步,即举例手推一遍代码,过程不表。以下是代码:

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        dp[2] = 1;
        for (int i = 3; i <= n; ++i) {
            for (int j = 0; j < i - 1; ++j) {
                dp[i] = Math.max(dp[i], Math.max(dp[i - j] * j, (i - j) * j));
            }
        }
        return dp[n];
    }
}

然后此题还有一种巧妙的数学解法,通过数学推导可得出在将该整数尽可能多的拆分出3以后即可使得答案最大。原题解在此

class Solution {
    public int integerBreak(int n) {
        if (n < 4) return n - 1;
        int a = n / 3, b = n % 3;
        if (b == 0) return (int) Math.pow(3, a);
        if (b == 1) return (int) Math.pow(3, a - 1) * 4;
        return (int) Math.pow(3, a) * 2;
    }
}

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

按照动态规划的五部曲来。

首先确定dp数组及下标的含义,即dp[i]表示为i个节点可组成的不同二叉搜索树的种类。

然后确定dp数组的递归公式。这里可以思考一下,如果计算的是dp[3],即树中有三个节点,那么dp[3] = dp[以节点3为根节点的二叉搜索树的数量] + dp[以节点1为根节点的二叉搜索树的数量] + dp[以节点2为根节点的二叉搜索树的数量]。即可以更深的理解为,dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量],其中j的取值范围为1到i。

第三步是初始化这个数组。很容易想到一个节点时只有一种情况,两个节点是两种不同的树。实际上初始化dp[1] = 1即可。

第四步是确定遍历顺序,从公式来看,当前计算的i依赖于此前计算出来的i - j的dp值,因此需要从前往后遍历。

到这里已经可以写出完整的代码了,但不妨进行第五步,手写计算一下验证代码是否正确。

以下是代码。

class Solution {
    public int numTrees(int n) {
        int[] dp = new int[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];
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值