代码随想录算法训练营第四十四天| 139. 单词拆分、 56. 携带矿石资源(第八期模拟笔试)、198. 打家劫舍、213. 打家劫舍 II、337. 打家劫舍 III、121. 买卖股票的最佳时机

[LeetCode] 139. 单词拆分

[LeetCode] 139. 单词拆分 文章解释

[LeetCode] 139. 单词拆分 视频解释

题目:

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true

注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。

示例 1:

输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以由 "leet" 和 "code" 拼接成。

示例 2:

输入: s = "applepenapple", wordDict = ["apple", "pen"]
输出: true
解释: 返回 true 因为 "applepenapple" 可以由 "apple" "pen" "apple" 拼接成。
     注意,你可以重复使用字典中的单词。

示例 3:

输入: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"]
输出: false

提示:

  • 1 <= s.length <= 300
  • 1 <= wordDict.length <= 1000
  • 1 <= wordDict[i].length <= 20
  • s 和 wordDict[i] 仅由小写英文字母组成
  • wordDict 中的所有字符串 互不相同

[LeetCode] 139. 单词拆分 

自己看到题目的第一想法

    回溯算法,每次回溯都遍历一遍 wordDict 中的每个单词,将所有单词拼接起来,如果长度超过目标字符串,则表示失败返回 false。否则判断字符串是否相等,相等则返回 true。

   但是动态规划章节肯定是要用动态规划啦,只能是从 wordDict 中不停的取单词,看最终是否能拼凑成目标字符串,看起来像是完全背包的问题。但是背包问题都是要有个容量,这里容量该怎么选择呢?从所有字符串中不停选取元素,看最终是否能凑成长度为 n 的字符串。 容量就是 1 个字符组成的字符串、2个字符组成的字符串,一直到 n 个字符组成的字符串。

   那物品呢,就是字典里的字符了。

看完代码随想录之后的想法

    迷迷糊糊的~

// 一维数组版本
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i < dp.length; i++) {
            // 对于字符串 s 的 0~i 区间,都需要对 wordDict 遍历一遍
            // 同时如果 dp[i] 已经可以被拼接上的话,不管到底拼接的组合是怎么样的,不需要管剩下的单词了
            for (int j = 0; j < wordDict.size() && !dp[i]; j++) {
                if (i < wordDict.get(j).length()) {
                    continue;
                }
                int startIndex = s.indexOf(wordDict.get(j), i - wordDict.get(j).length());
                if (startIndex != -1 && startIndex == i - wordDict.get(j).length()) {
                    dp[i] = dp[i - wordDict.get(j).length()];
                }
            }
        }
        return dp[s.length()];
    }
}
// 二维数组版本:效率很低。
class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        boolean[][] dp = new boolean[wordDict.size()][s.length() + 1];
        for (int i = 0; i < dp.length; i++) {
            dp[i][0] = true;
        }
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < dp.length; j++) {
                for (int k = 0; k < wordDict.size() && !dp[j][i]; k++) {
                    if (i < wordDict.get(k).length()) {
                        if (j >= 1) {
                            dp[j][i] = dp[j - 1][i];
                        }
                        continue;
                    }
                    int startIndex = s.indexOf(wordDict.get(k), i - wordDict.get(k).length());
                    if (startIndex == i - wordDict.get(k).length()) {
                        dp[j][i] = dp[j][startIndex];
                    } else {
                        if (j - 1 >= 0) {
                            dp[j][i] = dp[j - 1][i];
                        }
                    }
                }
            }
        }
        return dp[dp.length - 1][s.length()];
    }
}

 自己实现过程中遇到哪些困难

    随想录网站上的算法版本,一开始一直想不清楚和背包的关系,但是首先代码一定是能通过的,因此一定是满足了背包的定义。后来自己用一维和二维都实现了一遍,其实 for (j = 0; j <=i; j++) 的部分,就是在找普通完全背包里的 nums[i], 找到后才能找到递推的位置

[KamaCoder] 56. 携带矿石资源(第八期模拟笔试)

[KamaCoder] 56. 携带矿石资源(第八期模拟笔试)文章解释

题目描述

你是一名宇航员,即将前往一个遥远的行星。在这个行星上,有许多不同类型的矿石资源,每种矿石都有不同的重要性和价值。你需要选择哪些矿石带回地球,但你的宇航舱有一定的容量限制。 

给定一个宇航舱,最大容量为 C。现在有 N 种不同类型的矿石,每种矿石有一个重量 w[i],一个价值 v[i],以及最多 k[i] 个可用。不同类型的矿石在地球上的市场价值不同。你需要计算如何在不超过宇航舱容量的情况下,最大化你所能获取的总价值。

输入描述

输入共包括四行,第一行包含两个整数 C 和 N,分别表示宇航舱的容量和矿石的种类数量。 

接下来的三行,每行包含 N 个正整数。具体如下: 

第二行包含 N 个整数,表示 N 种矿石的重量。 

第三行包含 N 个整数,表示 N 种矿石的价格。 

第四行包含 N 个整数,表示 N 种矿石的可用数量上限。

输出描述

输出一个整数,代表获取的最大价值。 

输入示例
10 3
1 3 4
15 20 30
2 3 2
输出示例
90
提示信息

数据范围:
1 <= C <= 10000; 
1 <= N <= 10000; 
1 <= w[i], v[i], k[i] <= 10000;

[KamaCoder] 56. 携带矿石资源(第八期模拟笔试) 

​​​​​​​​​​​​​​自己看到题目的第一想法

    无

看完代码随想录之后的想法

    相对于 01 背包,每次计算当前容量可以装的最大价值时,不仅要考虑到 dp[j - weights[i]] + values[i], 还要考虑到直接放入 k 个的情况,即是 dp[j - k * weights[i]] + k * values[i]

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        /**
         * bagWeight:背包容量
         * n:物品种类
         */
        int bagWeight, n;
        
        //获取用户输入数据,中间用空格隔开,回车键换行
        bagWeight = sc.nextInt();
        n = sc.nextInt();
        int[] weight = new int[n];
        int[] value = new int[n];
        int[] nums = new int[n];
        
        for (int i = 0; i < n; i++) weight[i] = sc.nextInt();
        for (int i = 0; i < n; i++) value[i] = sc.nextInt();
        for (int i = 0; i < n; i++) nums[i] = sc.nextInt();
        
        int[] dp = new int[bagWeight + 1];
        for (int i = 0; i < weight.length; i++) {
            // 01 背包问题记得要倒序
            for (int j = bagWeight; j >= weight[i]; j--) {
                for (int k = 1; k <= nums[i] && j >= k * weight[i]; k++) {
                    dp[j] = Math.max(dp[j], dp[j - k * weight[i]] + k * value[i]);
                }
            }
        }
        System.out.println(dp[bagWeight]);
    }
}

自己实现过程中遇到哪些困难

    1. 忘记了多重背包的 k 循环以及对应 j >= k * weight[i] 的判断

    2. 01 背包是需要倒序遍历的

[LeetCode] 198. 打家劫舍

[LeetCode] 198. 打家劫舍 文章解释

[LeetCode] 198. 打家劫舍 视频解释​​​​​​​

题目:

你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警

给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。

示例 1:

输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 2:

输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
     偷窃到的最高金额 = 2 + 9 + 1 = 12 。

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 400

[LeetCode] 198. 打家劫舍 

自己看到题目的第一想法

    好像挺复杂的,有点像分糖果一样,顾此失彼。 

看完代码随想录之后的想法

    好简单, 一个递推公式搞定。

class Solution {
    public int rob(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        dp[1] = Math.max(dp[0], nums[1]);
        for (int i = 2; i < nums.length; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
        }
        return dp[nums.length - 1];
    }
}

自己实现过程中遇到哪些困难

    代码还是比较简单的, 注意一个 nums.length ==  1 的情况.

[LeetCode] 213. 打家劫舍 II
[LeetCode] 213. 打家劫舍 II 文章解释

[LeetCode] 213. 打家劫舍 II 视频解释

题目:

你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。

给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,今晚能够偷窃到的最高金额。

示例 1:

输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。

示例 2:

输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
     偷窃到的最高金额 = 1 + 3 = 4 。

示例 3:

输入:nums = [1,2,3]
输出:3

提示:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 1000

[LeetCode] 213. 打家劫舍 II

自己看到题目的第一想法

    不会~

看完代码随想录之后的想法

    好简单~

class Solution {

    public int rob(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }
        return Math.max(rob(nums, 0, nums.length - 2), rob(nums, 1, nums.length - 1));
    }

    private int rob(int[] nums, int start, int end) {
        if (start == end) {
            return nums[start];
        }
        int[] dp = new int[end - start + 1];
        dp[0] = nums[start];
        dp[1] = Math.max(nums[start + 1], dp[0]);// 别忘了要比较 0 和 1 取最大值
        for (int i = 2; i <= end - start; i++) {
            dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[start + i]);
        }
        return dp[dp.length - 1];
    }
}

自己实现过程中遇到哪些困难

    稍微有点没绕过来,整体还好。

[LeetCode] 337. 打家劫舍 III
[LeetCode] 337. 打家劫舍 III 文章解释

[LeetCode] 337. 打家劫舍 III 视频解释

题目:

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。

除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。

给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。

示例 1:

输入: root = [3,2,3,null,3,null,1]
输出: 7 
解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7

示例 2:

输入: root = [3,4,5,1,3,null,1]
输出: 9
解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9

提示:

  • 树的节点数在 [1, 10^4] 范围内
  • 0 <= Node.val <= 10^4

[LeetCode] 337. 打家劫舍 III

自己看到题目的第一想法

    一脸懵逼。

看完代码随想录之后的想法

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public int rob(TreeNode root) {
        // dp[0] 不偷当前节点的最大值, dp[1] 偷当前节点的最大值
        int[] dp = traversal(root);
        return Math.max(dp[0], dp[1]);
    }
    private int[] traversal(TreeNode node) {
        if (node == null) {
            return new int[2];
        }
        int[] left = traversal(node.left);
        int[] right = traversal(node.right);
        
        int [] dp = new int[2];
        dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
        dp[1] = node.val + left[0] + right[0];
        return dp;
    }
}

自己实现过程中遇到哪些困难

    当不偷自己的时候,要知道左孩子和右孩子,每个孩子返回的值中,要选择其中大的值。

[LeetCode] 121. 买卖股票的最佳时机
[LeetCode] 121. 买卖股票的最佳时机 文章解释

[LeetCode] 121. 买卖股票的最佳时机 视频解释

题目:

给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。

你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。

返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。

示例 1:

输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。

示例 2:

输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。

提示:

  • 1 <= prices.length <= 10^5
  • 0 <= prices[i] <= 10^4

[LeetCode] 121. 买卖股票的最佳时机 

自己看到题目的第一想法

    做贪心的时候看过,当时看的是可以多次买卖的版本,因此计算 dp[i][0] 的时候,写成 dp[i - 1][1] - prices[i],这是不对的。

看完代码随想录之后的想法

    可以多次买卖虽然能理解背后的数学本质,但真的挺难想的。总感觉掌握不住。

class Solution {
    public int maxProfit(int[] prices) {
        // dp[i][0] 表示第 i 天持有股票,账户中剩余的资金
        // dp[i][1] 表示第 i 天不持有股票,账户中剩余的资金
        int[][] dp = new int[prices.length][2];
        dp[0][0] = -prices[0];
        dp[0][1] = 0;
        for (int i = 1; i < dp.length; i++) {
            dp[i][0] = Math.max(dp[i - 1][0], - prices[i]);
            dp[i][1] = Math.max(dp[i - 1][1], prices[i] + dp[i - 1][0]);
        }
        return dp[prices.length - 1][1];
    }
}
class Solution {
    public int maxProfit(int[] prices) {
        int result = 0;
        int low = prices[0];
        for (int i = 1; i < prices.length; i++) {
            if (prices[i] < low) {
                low = prices[i];
            } else if (prices[i] - low > result) {
                result = prices[i] - low;
            }
        }
        return result;
    }
}

自己实现过程中遇到哪些困难 

    递推公式写错啦~

  • 20
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第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、付费专栏及课程。

余额充值