【力扣】从零开始的动态规划

这篇具有很好参考价值的文章主要介绍了【力扣】从零开始的动态规划。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【力扣】从零开始的动态规划

开头

本力扣题解用5题来引出动态规划的解题步骤,用于本人进阶掌握动态规划,在刷题过程中写下的一些解题步骤与思路,供大家一起学习

139. 单词拆分

139. 单词拆分

给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s

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

解题思路

状态表示: dp[i]表示字符串以0到i-1的字符串,能否组成字串

初始状态:dp[0]=true,没有字符串的情况肯定为true,如果这个为false,那么后面全部为false

状态转移方程:

​ 可以把一个字符串来看成两段,0~j-1j~i,前面一半可以看成dp[j],因为看下状态表示就知道了,dp[i]表示字符串以0到i-1的字符串, 带入j得dp[j]表示字符串以0到j-1的字符串。

​ 后一半直接在哈希表中找子串是否存在,找到了就是true,如果两个字串同时为true,那么dp[i]=true

​ 因为以0~i的字符串的分解的情况有很多种,只要其中一种为true,那么就是true,直接break

​ 所以动态转移方程为:

            if(dp[j] && set.contains(s.substring(j,i)))
            {
                dp[i]=true;
                break;
            }
import java.util.HashSet;
import java.util.Set;

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set=new HashSet<>(wordDict);
        int n=s.length();
        boolean[] dp=new boolean[n+1];
        dp[0]=true;
        //从第i个字符结束的
        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<i;j++)
            {
                if(dp[j] && set.contains(s.substring(j,i)))
                {
                    dp[i]=true;
                    break;
                }
            }
        }
        return dp[n];
    }
}

45. 跳跃游戏 II

45. 跳跃游戏 II

给定一个长度为 n0 索引整数数组 nums。初始位置为 nums[0]

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

  • 0 <= j <= nums[i]
  • i + j < n

返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]

解题思路

状态表示: dp[i]表示从1开始跳到第i个数的最小次数

​ **初始状态:**第1个元素是起点,可以到达,其他所有结点默认无法到达,设置一个很大的初始值

状态转移方程:

​ 第i个数可以从前面任意一个跳跃距离大于两个结点距离的结点,及判断条件if(nums[j]>=i-j),在这些满足要求的结点中取最小值,方程为

dp[i]=min(dp[0],dp[1],dp[2]...dp[n-1])+1,写成循环结构,最终方程为:dp[i]=Math.min(dp[i],dp[j]+1);

import java.util.Arrays;

class Solution {
    public int jump(int[] nums) {
        int n=nums.length;
        int[] dp=new int[n];
        Arrays.fill(dp,Integer.MAX_VALUE);
        //状态表示:前跳到第i个的最小次数
        dp[0]=0;
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<i;j++)
            {
                int l=i-j;
                if(nums[j]>=l)
                {
                    dp[i]=Math.min(dp[i],dp[j]+1);
                }

            }
        }
        return dp[n-1];
    }
}

5. 最长回文子串

5. 最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。

如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。

解题思路

状态表示: dp[i][j] 表示 i到j是否是一个回文串

​ **初始状态:**无

状态转移方程:

​ 想要知道dp[i][j]是否是一个回文子串,只需知道dp[i+1][j-1]是否是一个回文子串并且外层的字符相等即s[i]==s[j],那么dp[i][j]就是一个回文子串,还有一种特殊情况是:要判断的字符串只有两个字符时,不用再判断dp[i+1][j-1]是否是一个回文子串,只需判断这两个字符是否相等即可

​ **循环顺序:**想要知道循环顺序是从大到小,还是从小到大,我们要知道dp数组中哪个要先算出来 ,哪个后算出来,比如想要知道dp[i][j]是否是一个回文子串,就得先知道dp[i+1][j-1]是否也是一个回文子串,所以dp[i+1][j-1]要被先计算出来,分为两重循环来分析

​ 外重循环i+1比i要先知道,所以,i从大到小循环

​ 内重循环j-1比j要先知道,所以j从小到大循环

​ 又因为j一定要大于等于i,因为范围表示是i~j,所以j从i到n-1

            if(s.charAt(i)==s.charAt(j))
            {
                if(j-i<2 || dp[i+1][j-1] )
                    dp[i][j]=true;
            }
class Solution {
    public String longestPalindrome(String s) {
        int n=s.length();
        //状态表示:i到j是否是一个回文串
        boolean[][] dp=new boolean[n][n];

        //dp[i][j]=dp[i+1][j-1] && s.charAt(i)==s.charAt(j)
        String str="";
        for(int i=n-1;i>=0;i--)
        {
            for(int j=i;j<n;j++)
            {
                if(s.charAt(i)==s.charAt(j))
                {
                    if(j-i<2 || dp[i+1][j-1] )
                        dp[i][j]=true;
                }
                if(dp[i][j] && (j-i+1)>str.length())
                {
                    str=s.substring(i,j+1);
                }
            }
        }
        return str;
    }
}

1143. 最长公共子序列

1143. 最长公共子序列

给定两个字符串 text1text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

  • 例如,"ace""abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。

两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

解题思路

状态表示:dp[i][j] 表示 text1[0:i-1]text2[0:j-1] 的最长公共子序列

**状态转移方程:**知道状态定义之后,我们开始写状态转移方程。

​ 当 text1[i - 1] == text2[j - 1] 时,说明两个子字符串的最后一位相等,所以最长公共子序列又增加了 1,所以 dp[i][j] = dp[i - 1][j - 1] + 1;举个例子,比如对于 ac 和 bc 而言,他们的最长公共子序列的长度等于 a 和 b 的最长公共子序列长度 0 + 1 = 1。
text1[i - 1] != text2[j - 1] 时,说明两个子字符串的最后一位不相等,那么此时的状态 dp[i][j] 应该是 dp[i - 1][j]dp[i][j - 1] 的最大值。举个例子,比如对于 ac和 bc 而言,他们的最长公共子序列的长度等于 ① ace 和 b 的最长公共子序列长度0 与 ② ac 和 bc 的最长公共子序列长度1 的最大值,即 1。

​ 所以状态转移方程为:当text[i-1]==text[j-1]时.dp[i][j]=dp[i-1][j-1]+1

​ 当text[i-1]!=text[j-1]时,dp[i][j]=max(dp[i−1][j],dp[i][j−1])

**初始值:**初始化就是要看当 i = 0 与 j = 0 时, dp[i][j] 应该取值为多少。

当 i = 0 时,dp[0][j] 表示的是 text1 中取空字符串 跟 text2 的最长公共子序列,结果肯定为 0.
当 j = 0 时,dp[i][0] 表示的是 text2 中取空字符串 跟 text1 的最长公共子序列,结果肯定为 0.
综上,当 i = 0 或者 j = 0 时,dp[i][j] 初始化为 0.

遍历方向与范围:由于 dp[i][j] 依赖与 dp[i - 1][j - 1] , dp[i - 1][j], dp[i][j - 1],所以 iii 和 jjj 的遍历顺序肯定是从小到大的。 另外,由于当 i 和 j 取值为 0 的时候,dp[i][j] = 0,而 dp 数组本身初始化就是为 0,所以,直接让 i 和 j 从 1 开始遍历。遍历的结束应该是字符串的长度为 len(text1) 和 len(text2)

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int n=text1.length();
        int m=text2.length();
        int[][] dp=new int[n+1][m+1];
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                if(text1.charAt(i-1)==text2.charAt(j-1))
                    dp[i][j]=dp[i-1][j-1]+1;
                else
                    dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1]);
            }
        }
        return dp[n][m];
    }
}

931. 下降路径最小和

931. 下降路径最小和

给你一个 n x n方形 整数数组 matrix ,请你找出并返回通过 matrix下降路径最小和

下降路径 可以从第一行中的任何元素开始,并从每一行中选择一个元素。在下一行选择的元素和当前行所选元素最多相隔一列(即位于正下方或者沿对角线向左或者向右的第一个元素)。具体来说,位置 (row, col) 的下一个元素应当是 (row + 1, col - 1)(row + 1, col) 或者 (row + 1, col + 1)

示例 1:

【力扣】从零开始的动态规划,力扣刷题集,leetcode,动态规划,算法

输入:matrix = [[2,1,3],[6,5,4],[7,8,9]]
输出:13
解释:如图所示,为和最小的两条下降路径

解题思路

状态表示:dp[i][j]表示走到matrix[i][j]的最小下降和

**初始值:**第一行全是0,因为在原地(起点)

**状态转移方程:**到达dp[i][j]可以从上一层的相邻元素到达,取其中的最小值并加上一步的数量,即dp[i][j]=min(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1])+matrix[i][j],因为要判断边界条件,在最左边和最右边只能从上一层的两个到达。

​ 综上所诉,状态转移方程为:

if(j==0)
    dp[i][j]=Math.min(dp[i-1][j],dp[i-1][j+1])+matrix[i][j];
else if(j==m-1)
    dp[i][j]=Math.min(dp[i-1][j-1],dp[i-1][j])+matrix[i][j];
else
    dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i-1][j+1]))+matrix[i][j];

**答案:**根据题目要求,到达最后一行的最少下降和,再看状态表示:dp[i][j]表示走到matrix[i][j]的最小下降和,所以答案就在dp的最后一行中,取最后一行的最小值即是答案文章来源地址https://www.toymoban.com/news/detail-756750.html

class Solution {
    public int minFallingPathSum(int[][] matrix) {
        int n=matrix.length;
        int m=matrix[0].length;
        int[][] dp=new int[n][m];
        for(int j=0;j<m;j++)
        {
            dp[0][j]=matrix[0][j];
        }
        for(int i=1;i<n;i++)
        {
            for(int j=0;j<m;j++)
            {
                if(j==0)
                    dp[i][j]=Math.min(dp[i-1][j],dp[i-1][j+1])+matrix[i][j];
                else if(j==m-1)
                    dp[i][j]=Math.min(dp[i-1][j-1],dp[i-1][j])+matrix[i][j];
                else
                    dp[i][j]=Math.min(dp[i-1][j-1],Math.min(dp[i-1][j],dp[i-1][j+1]))+matrix[i][j];
            }
        }
        int min=Integer.MAX_VALUE;
        for(int j=0;j<m;j++)
        {
            min=Math.min(min,dp[n-1][j]);
        }
        return min;
    }
}

到了这里,关于【力扣】从零开始的动态规划的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零开始的力扣刷题记录-第七十二天

    题目描述: 给你一个整数数组 nums ,它包含 2 * n 个整数。 你需要将 nums 划分成 n 个数对,满足: 每个元素 只属于一个 数对。 同一数对中的元素 相等 。 如果可以将 nums 划分成 n 个数对,请你返回 true ,否则返回 false 题解: 哈希表统计各元素数量,如果有不能被2整除的就

    2024年02月11日
    浏览(70)
  • 从零开始的力扣刷题记录-第五十八天

    题目描述: 给你一个 不包含 任何零的整数数组 nums ,找出自身与对应的负数都在数组中存在的最大正整数 k 。 返回正整数 k ,如果不存在这样的整数,返回 -1 。 题解: 哈希表存储负数,再遍历nums对每一个正数去哈希表中查找是否存在对应的负数。存在就更新返回值 代码

    2024年02月09日
    浏览(47)
  • 从零开始的力扣刷题记录-第八十七天

    题目描述: 给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字: 例如,从根节点到叶节点的路径 1 - 2 - 3 表示数字 123 。 计算从根节点到叶节点生成的 所有数字之和 。 叶节点 是指没有子节点的节点

    2024年02月07日
    浏览(46)
  • 从零开始的力扣刷题记录-第四十八天

    给你一个下标从 0 开始的数组 nums ,数组大小为 n ,且由 非负 整数组成。 你需要对数组执行 n - 1 步操作,其中第 i 步操作(从 0 开始计数)要求对 nums 中第 i 个元素执行下述指令: 如果 nums[i] == nums[i + 1] ,则 nums[i] 的值变成原来的 2 倍,nums[i + 1] 的值变成 0 。否则,跳过

    2024年02月09日
    浏览(51)
  • 从零开始的力扣刷题记录-第六十一天

    题目描述: 给定由一些正数(代表长度)组成的数组 nums ,返回 由其中三个长度组成的、面积不为零的三角形的最大周长 。如果不能形成任何面积不为零的三角形,返回 0。 题解: 排序后从后往前遍历,取最大的三个边,如果满足两边之和大于第三边则返回,否则整体向前

    2024年02月09日
    浏览(57)
  • 从零开始的力扣刷题记录-第六十四天

    题目描述: 给你一个下标从 0 开始的整数数组 nums 。在一步操作中,你可以执行以下步骤: 从 nums 选出 两个 相等的 整数 从 nums 中移除这两个整数,形成一个 数对 请你在 nums 上多次执行此操作直到无法继续执行。 返回一个下标从 0 开始、长度为 2 的整数数组 answer 作为答

    2024年02月11日
    浏览(45)
  • 从零开始的力扣刷题记录-第四十四天

    题目描述: 和谐数组是指一个数组里元素的最大值和最小值之间的差别 正好是 1 。 现在,给你一个整数数组 nums ,请你在所有可能的子序列中找到最长的和谐子序列的长度。 数组的子序列是一个由数组派生出来的序列,它可以通过删除一些元素或不删除元素、且不改变其余

    2024年02月07日
    浏览(55)
  • 从零开始的力扣刷题记录-第五十一天

    题目描述: 给你一棵二叉搜索树的 root ,请你 按中序遍历 将其重新排列为一棵递增顺序搜索树,使树中最左边的节点成为树的根节点,并且每个节点没有左子节点,只有一个右子节点。 题解: 中序遍历存储节点后按顺序连接即可 代码(Go): 题目描述: 小扣在秋日市集发

    2024年02月08日
    浏览(44)
  • 【力扣刷题】整数拆分(动态规划)

    个人简历: 全栈领域新星博主, 万粉博主、 帮助初学者入门,记录自己的学习过程 个人主页:天寒雨落的博客_CSDN博客-C,CSDN竞赛,python领域博主 热门专栏:初学者入门C语言_天寒雨落的博客-CSDN博客   目录 动态规划 整数拆分 题目 思路 代码 执行结果 其基本思想是将待求解

    2024年02月03日
    浏览(44)
  • 力扣刷题-动态规划算法3:完全背包问题

    问题描述: 1)有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。 2) 每件物品都有无限个(也就是可以放入背包多次) (比0-1背包多出的条件) 3) 求解将哪些物品装入背包里物品价值总和最大。 求解步骤: 1)首先遍历物品,然

    2023年04月13日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包