代码随想录算法训练营第41天 || 343. 整数拆分 || 96.不同的二叉搜索树

代码随想录算法训练营第41天 || 343. 整数拆分 || 96.不同的二叉搜索树

343. 整数拆分

题目介绍:

给定一个正整数 n ,将其拆分为 k正整数 的和( k >= 2 ),并使这些整数的乘积最大化。

返回 你可以获得的最大乘积

示例 1:

输入: n = 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:

输入: n = 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

个人思路:

说实话,我的这种方法并不是动态规划,算是数学规律常识法,也可能有点贪心的思路吧。

不难发现,我们将一个数分成k份时,必然是分的数越平均,乘积越大(不证明了),不能直接完全均分,余数也要均分,然后再将所有数相乘即可得到分成k份的最大乘积值,例如将10分成3份可以10/3 = 3; 10%3 = 1;,所以,我们可以分成3 3 4,最大乘积为36

同时,我们也不难发现,k从2开始递增到n时,得到的每个最值乘积,必然呈现先增后减的单调性。所以我们只需要遍历到非递增的位置即可结束遍历,返回结果。

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        for (int i = 2; i < dp.length; i++) {
            dp[i] = 1;
            int num1 = n / i;//均分数
            int num2 = n % i;//均分后的余数
            //连乘i-num2个均分数
            for (int j = 0; j < i - num2; j++)
                dp[i] *= num1;
            //连乘num2个均分数+1的和
            for (int j = 0; j < num2; j++)
                dp[i] *= num1 + 1;
            //出现递减后面肯定都是递减,算是一种数学规律吧,先增后减
            if (dp[i] <= dp[i - 1])
                return dp[i - 1];
        }
        return dp[n];
    }
}

题解解析:

动规五部曲:

  1. 确定dp数组及其下标含义

    dp[i]:分拆数字i,得到的最大乘积dp[i]

  2. 确定递推公式

    dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));

    表示拆分数字i,循环递增j,比较dp[i](i - j) * jdp[i - j] * j

    • dp[i]:过程中不断刷新的最大值
    • (i - j) * j:拆分成ji-j两个数的情况
    • dp[i - j] * j:拆分成ji-j能拆成的最大乘积组合(三个数及以上)

    如此遍历下来,就能遍历完所有情况固定一个数j,然后其余数找到它的最大乘积组合;也就是两个数的组合和多个数的组合

    一些疑问:

    • 为什么j不拆分呢?
      • j在遍历过程中已经都计算过了,j * (i - j) 是单纯的把整数拆分为两个数相乘,而j * dp[i - j]是拆分成两个以及两个以上的个数相乘。
    • 为什么要比较这两个,(i - j) * jdp[i - j] * j,代表什么含义呢
      • 前者表示两个数的组合乘积,后者表示至少三个数的最大乘积
      • 我们不要忘了,拆分至少分成两份
  3. 初始化dp数组

    下标0、1处没必要去初始化,没有意义(虽然也可以过)

    我们只需要初始化dp[2] = 1即可

  4. 确定遍历顺序

    由递推公式dp[i] = max(dp[i], max((i - j) * j, dp[i - j] * j));可知我们正序遍历,内层也设置一个for循环遍历j即可

  5. 打印dp数组检验

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        //dp[i]表示i拆分得到的最大乘积数
        dp[2] = 1;
        for (int i = 3; i <= n; i++) {
            for (int j = 1; j <= i / 2; j++) {//这里遍历到i/2即可,因为再往后不符合均分最大常识
                //分别表示遍历过的最大值、拆成两个数的最大乘积、拆成至少3个数的最大乘积
                dp[i] = Integer.max(dp[i], Integer.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

96.不同的二叉搜索树

题目介绍:

给你一个整数 n ,求恰由 n 个节点组成且节点值从 1n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。

示例 1:

img

输入:n = 3
输出:5

示例 2:

输入:n = 1
输出:1

个人思路:

先画出n = 1n = 4的二叉搜索树情况,分析规律

不难发现,n确定时,选择根节点不同,它的左右子树的节点数量就不同,然后一不小心发现,子树的搜索树数量可以用之前的dp数组得到,故得到递推公式dp[i] += dp[j] * dp[i - 1 - j];

动规五部曲:

  1. 确定dp数组及其下标含义

    int[] dp = new int[n + 1];
    //dp[i]表示i个结点有多少种二叉搜索树
    
  2. 确定递推公式

    //遍历左右子树不同数量的各种情况,子树的搜索树数量可以找之前的dp数组
    for (int j = 0; j < i; j++) 
        dp[i] += dp[j] * dp[i - 1 - j];//注意i-1
    
  3. 初始化dp数组

    dp[0] = 1;//相当于某一孩子结点为null,也就是它也算是一种情况
    
  4. 确定遍历顺序

    正常正序遍历即可

  5. 打印dp数组检验

代码:

class Solution {
    public int numTrees(int n) {
        int[] dp = new int[n + 1];//dp[i]表示i个结点有多少种二叉搜索树
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            //遍历左右子树不同数量的各种情况,子树的搜索树数量可以找之前的dp数组
            for (int j = 0; j < i; j++) {
                dp[i] += dp[j] * dp[i - 1 - j];
//                System.out.println(i + "  " + dp[i]);
            }
        }
        return dp[n];
    }
}

= 0; j < i; j++) {
dp[i] += dp[j] * dp[i - 1 - j];
// System.out.println(i + " " + dp[i]);
}
}
return dp[n];
}
}




代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值