【动态规划】动态规划经典例题 力扣牛客

news2024/11/26 23:46:53

文章目录

      • 跳台阶 BM63 简单
      • 跳台阶扩展 JZ71 简单
      • 打家结舍 LC198 中等
      • 打家劫舍2 LC213中等
      • 最长连续递增序列 LC674 简单
      • 乘积最大子数组LC152 中等
      • 最长递增子序列LC300 中等
      • 最长重复子数组LC718
      • 最长公共子串NC BM66
      • 最长公共子序列LC1143 中等
      • 完全平方数LC279
      • 零钱兑换 LC322 中等
      • 单词拆分LC139 中等
      • 编辑距离LC72 困难
      • 买卖股票的最佳时机LC121 简单
      • 买卖股票的最佳时机2 LC122中等
      • 不同路径LC62 中等
      • 最小路径和LC64 中等
      • 分割等和子集LC416 中等

跳台阶 BM63 简单

题目链接
在这里插入图片描述
代码:

public class Solution {

    public int jumpFloor (int number) {
        
        if(number==1||number==2)return number;
        // 跳一步或两步
        else return jumpFloor(number-1) + jumpFloor(number-2);
    }
}

跳台阶扩展 JZ71 简单

题目链接
描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶(n为正整数)总共有多少种跳法。
在这里插入图片描述
思路:

  • F(n) = F(n-1)+F(n-2)+F(n-3)+…F(1)+1=F(n-1)+F(n-1) = 2*F(n-1)
  • F(3)=F(2)+F(1)+1

跳上第3阶台阶,可以从第2阶、第1阶跳上去,也可以从第0阶直接1步跳上第3阶。

代码1:

public class Solution {
    // 当前无优化,可添加记忆数组优化时间复杂度
    public int jumpFloorII (int number) {

        if(number == 1) return 1;
        int count = 1;
        for(int i = 1;i < number;i++){
            count+=jumpFloorII(number-i);
        }
        return count;
    }
}

代码2:

public class Solution {
    
    public int jumpFloorII (int number) {

        if(number == 1) return 1;
        return 2*jumpFloorII(number-1);
    }
}

打家结舍 LC198 中等

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

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

示例 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 。

代码:

class Solution {
    int[] memory;// 备忘录

    public int rob(int[] nums) {
        int len = nums.length;
        memory = new int[len];
        Arrays.fill(memory,-1);// 注意填充-1

        return DP(nums,0,len-1,0);
    }
    // 能打劫的范围[start,end],从i家开始选择是否打劫
    public int DP(int[] nums,int start,int end,int i){
        if(i==end)return nums[i];
        if(i>end)return 0;

        if(memory[i]!=-1)return memory[i];// 查找备忘录

        memory[i] = Math.max(
            DP(nums,start,end,i+1),// 不选
            nums[i]+DP(nums,start,end,i+2) // 选 隔一家在决定选择
        );
        return memory[i];
    }
}

打家劫舍2 LC213中等

题目链接

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

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

示例 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 2 3 4 5围成一圈,即1与5不能同时选,则可看作是从1 2 3 4里选以及从2 3 4 5里选,取两者最大即可。

代码:

class Solution {
    int[] memory;// 备忘录

    public int rob(int[] nums) {


        int len = nums.length;
        if(len==1)return nums[0];//base case 

        memory = new int[len];

        Arrays.fill(memory,-1);// 注意填充-1
        int r1 =  DP(nums,0,len-2,0);

        Arrays.fill(memory,-1);// 注意填充-1
        int r2 =  DP(nums,1,len-1,1);

        return Math.max(r1,r2);

    }
    // 能打劫的范围[start,end],从i家开始选择是否打劫
    public int DP(int[] nums,int start,int end,int i){
        if(i==end)return nums[i];//base
        if(i>end)return 0;//base

        if(memory[i]!=-1)return memory[i];// 查找备忘录

        memory[i] = Math.max(
            DP(nums,start,end,i+1),// 不选
            nums[i]+DP(nums,start,end,i+2) // 选 隔一家在决定选择
        );
        return memory[i];
    }
}

最长连续递增序列 LC674 简单

题目链接
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。

连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], …, nums[r - 1], nums[r]] 就是连续递增子序列。

示例 1:

输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。 
示例 2:

输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。

代码:

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if(nums.length==1)return 1;

        int finalresult = 0;
        // 以下标 i为结尾的数组的连续递增的子序列长度为 dp[i]
        int[] dp = new int[nums.length]; 
        Arrays.fill(dp,1);
        for(int i = 1; i < nums.length;i++){
            if(nums[i-1]<nums[i]){
                dp[i] = dp[i-1]+1;
            }
            finalresult = Math.max(finalresult, dp[i]);
        }
        return finalresult;
    }
}

class Solution {
    public int findLengthOfLCIS(int[] nums) {
        if(nums.length==1)return 1;

        int finalresult = 0;
        int temresult = 1;
        for(int i = 0; i < nums.length-1;i++){
            if(nums[i]<nums[i+1]){
                temresult++;
            }else{
                temresult=1;
            }
            finalresult = Math.max(finalresult,temresult);
        }
        return finalresult;
    }
}

乘积最大子数组LC152 中等

题目链接

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

测试用例的答案是一个 32-位 整数。

子数组 是数组的连续子序列。

示例 1:

输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:

输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。

代码:

class Solution {
    public int maxProduct(int[] nums) {
        
        int len = nums.length;
        
        // 因为nums中存在负数,乘积最小的值乘以-1可能变为乘积最大

        int[] dpmin = new int[len];// 以i结尾的乘积最小子数组的乘积
        int[] dpmax = new int[len];// 以i结尾的乘积最大子数组的乘积


        dpmin[0] = nums[0];
        dpmax[0] = nums[0];
        for(int i = 1; i < len; i++){
            
            dpmin[i] = min(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i],nums[i]);
            dpmax[i] = max(dpmax[i-1]*nums[i],dpmin[i-1]*nums[i],nums[i]);
        }
        int result = dpmax[0];
        for(int r1:dpmax){
            result = result<r1?r1:result;
        } 
        return result;
    }

    int max(int i,int j,int k){
        return Math.max(i,Math.max(j,k));
    }
    int min(int i,int j,int k){
        return Math.min(i,Math.min(j,k));
    }
}

最长递增子序列LC300 中等

题目链接
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

示例 1:

输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:

输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:

输入:nums = [7,7,7,7,7,7,7]
输出:1

代码:

class Solution {
    public int lengthOfLIS(int[] nums) {
        
        int len = nums.length;

        int res = 1;
        //  以下标 i为结尾的数组的递增的子序列长度为 dp[i]
        int[] dp = new int[len];
        Arrays.fill(dp,1);

        for(int i = 0; i < len;i++){
            for(int j = 0;j < i;j++){
                if(nums[i]>nums[j]){
                    dp[i] = Math.max(dp[i],dp[j]+1);
                }
            }
        }
        for(int i = 0; i < len;i++){
            res = Math.max(res,dp[i]);
        }
        return res;
    }
}

最长重复子数组LC718

题目链接
给两个整数数组 nums1 和 nums2 ,返回 两个数组中 公共的 、长度最长的子数组的长度 。

示例 1:

输入:nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
输出:3
解释:长度最长的公共子数组是 [3,2,1] 。
示例 2:

输入:nums1 = [0,0,0,0,0], nums2 = [0,0,0,0,0]
输出:5

class Solution {
    public int findLength(int[] nums1, int[] nums2) {

        int[][] dp = new int[nums1.length+1][nums2.length+1];
        int finalresult = 0;
        // 两个数组,就是两个for
        for(int i = 1; i <= nums1.length; i++){ // 从nums1的第1个数字开始
            for(int j = 1; j <= nums2.length; j++){// 从nums2的第1个数字开始

                if(nums1[i-1]==nums2[j-1]){// 如果相同
                    dp[i][j] = dp[i-1][j-1]+1;
                }
                finalresult = Math.max(dp[i][j],finalresult);
            }
        }
        return finalresult;
    }
}

最长公共子串NC BM66

题目链接
在这里插入图片描述
题目与LC718相似

public class Solution {
    /**
     * @param str1 string字符串 the string
     * @param str2 string字符串 the string
     * @return string字符串
     */
    public String LCS (String str1, String str2) {
        // write code here
        if(str1.length()==0||str2.length()==0)return "";

        int[][] dp = new int[str1.length()+1][str2.length()+1];

        for(int i = 0;i < str1.length();i++){
            dp[i][0] = 0;
        }
        for(int i = 0;i < str2.length();i++){
            dp[0][i] = 0;
        }
        String str = "";
        int maxlen = 0;
        for(int i = 0; i < str1.length();i++){
            for(int j = 0; j < str2.length();j++){
                if(str1.charAt(i)==str2.charAt(j)){
                    dp[i+1][j+1] = dp[i][j]+1;
                    if(maxlen<dp[i+1][j+1]){
                        maxlen = dp[i+1][j+1];
                        str = str1.substring(i-maxlen+1,i+1);
                    }
                }
            }
        }
        return str;
    }
}

最长公共子序列LC1143 中等

题目链接
给定两个字符串 text1 和 text2,返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 ,返回 0 。

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

例如,“ace” 是 “abcde” 的子序列,但 “aec” 不是 “abcde” 的子序列。
两个字符串的 公共子序列 是这两个字符串所共同拥有的子序列。

示例 1:

输入:text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。
示例 2:

输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc" ,它的长度为 3 。
示例 3:

输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0 。

代码:

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
        int len1 = text1.length();
        int len2 = text2.length();

        int[][] dp = new int[len1+1][len2+1];
        for(int i = 0;i <= len1;i++){
            dp[i][0] = 0;
        }
        for(int i = 0;i <= len2;i++){
            dp[0][i] = 0;
        }
        for(int i = 1; i <= len1;i++){
            for(int j = 1;j <= len2;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[len1][len2];

    }
}

完全平方数LC279

给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。

完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。

示例 1:
输入:n = 12
输出:3 
解释:12 = 4 + 4 + 4

示例 2:
输入:n = 13
输出:2
解释:13 = 4 + 9

代码:

class Solution {
    public int numSquares(int n) {
        int[] dp = new int[n+1];
        Arrays.fill(dp,Integer.MAX_VALUE);
        dp[0] = 0;
        for(int i = 1; i <= n;i++){
            for(int j = 1; j*j <= i;j++){
                dp[i] = Math.min(dp[i],dp[i-j*j]+1);
            }
        }
        return dp[n];
    }
}

零钱兑换 LC322 中等

题目链接
给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。

计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。

你可以认为每种硬币的数量是无限的。

示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3 
解释:11 = 5 + 5 + 1

示例 2:
输入:coins = [2], amount = 3
输出:-1

示例 3:
输入:coins = [1], amount = 0
输出:0

代码:

class Solution {
    Map<Integer,Integer> memory = new HashMap<>();
    public int coinChange(int[] coins, int amount) {
        return DP(coins,amount);
    }
    
    public int DP(int[] coins,int target){

        if(target==0)return 0;
        if(target<0) return -1;
        // 查询备忘录
        if(memory.containsKey(target))return memory.get(target);

        int result = Integer.MAX_VALUE; // 注意此行位置
        for(int coin:coins){

            int n = DP(coins,target-coin);
             // 存储备忘录
            memory.put(target-coin,n);
            if(n == -1) continue;  // 注意此行
            result = Math.min(result,n+1);
            
        }

        return result == Integer.MAX_VALUE?-1:result; // 注意此行

    }
}

单词拆分LC139 中等

题目链接

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

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

示例 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 中的所有字符串 互不相同

方法1:遍历所有单词的长度查看是否匹配

class Solution {

    List<String> wordDict;
    int[] memory ;

    public boolean wordBreak(String s, List<String> wordDict1) {
        wordDict = wordDict1;
        memory = new int[s.length()+1];
        Arrays.fill(memory,-2);
        return DP(s,0);
    }


    boolean DP(String s,int i){
        if(i==s.length()){
            return true;
        }
        if(i>s.length()){
            return false;
        }

        if(memory[i]!=-2)return memory[i]==1?true:false;
   
        for(String word:wordDict){
            int len = word.length();
            if(i+len>s.length())continue;
            if(s.substring(i,i+len).equals(word)){
                boolean r1 = DP(s,i+len);
                if(r1==true){
                    memory[i]=1;
                    return true;
                }
            }
        }
         memory[i]=0;
        return false;
    }
}

方法2:遍历所有长度查看是否匹配

class Solution {


    List<String> wordDict1;
	int[] memory;
    public boolean wordBreak(String s, List<String> wordDict) {
		wordDict1 = wordDict;
		memory = new int[s.length()];
		Arrays.fill(memory,-1);
		return DP(s,0);
    }

	public boolean DP(String s,int i ){
		if(i == s.length())return true;

		if(memory[i]!=-1)return memory[i]==1?true:false;

		for (int slen = 1; slen + i <= s.length(); slen++) {
			String s1 = s.substring(i,i + slen);
			if(wordDict1.contains(s1)){

				boolean b = DP(s, i + slen);
				if(b == true){
					memory[i] = 1;
					return true;
				}
			}
		}
		memory[i] = 0;
		return false;
	}
}

编辑距离LC72 困难

题目链接

给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。

你可以对一个单词进行如下三种操作:

  • 插入一个字符
  • 删除一个字符
  • 替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3

解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

示例 2:
输入:word1 = "intention", word2 = "execution"
输出:5

解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')

思路:

在这里插入图片描述

在这里插入图片描述
代码:

class Solution {

    int[][] memory;

    public int minDistance(String word1, String word2) {
        memory = new int[word1.length()][word2.length()];

        for(int[] m:memory){
            Arrays.fill(m,-1);
        }

        return DP(word1,word1.length()-1,word2,word2.length()-1);
    }

    public int DP(String w1,int i,String w2,int j){
        if(i == -1) return j+1;
        if(j == -1) return i+1;

        if(memory[i][j]!=-1)return memory[i][j];

        if(w1.charAt(i) == w2.charAt(j)){
            int r = DP(w1,i-1,w2,j-1);
            memory[i][j] = r;
            return r;
        }
        else{

            int r = min(
                DP(w1,i ,w2,j-1),   // 插入:在w1的i的位置插入,j被匹配,j-1
                DP(w1,i-1,w2,j ),   // 删除:对w1的i的位置删除,i-1,j未匹配
                DP(w1,i-1,w2,j-1)   // 替换:对w1的i的位置替换,j被匹配,i替换后被匹配
                ) + 1;
            memory[i][j] = r;
            return r;

        }

    }

    int min(int i,int j,int x){
        return Math.min(i,Math.min(j,x));
    }
}

买卖股票的最佳时机LC121 简单

题目链接

给定一个数组 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。

设置一个二期数组dp[][],第一维表示天数,第二维表示是否持有股票。

class Solution {
    public int maxProfit(int[] prices) {
         
         int[][] dp = new int[prices.length][2]; // dp[i][1/0]:第i天持有1/不持有0

        for(int i = 0; i < prices.length;i++){
            
            if(i-1==-1){
                dp[i][0] = 0;
                dp[i][1] = -prices[0];
                continue;
            }
            
            // 未持有股票=max(昨天未持有股票,昨天持有股票今天卖出)
            dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
            // 持有股票=max(昨天持有股票,昨天未股票今天买入)
            dp[i][1] = Math.max(dp[i-1][1],-prices[i]);// 只能买一次,所以第一次买时所赚利润为0-price。
        }
        return dp[prices.length-1][0];
    }
}

买卖股票的最佳时机2 LC122中等

题目链接

给你一个整数数组 prices ,其中 prices[i] 表示某支股票第 i 天的价格。

在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买,然后在 同一天 出售。

返回 你能获得的 最大 利润 。

示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6 - 3 = 3 。
     总利润为 4 + 3 = 7 。

示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4 。
     总利润为 4 。

示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易可以获得最大利润,最大利润为 0 。

代码:

class Solution {
    public int maxProfit(int[] prices) {

        int[][] dp = new int[prices.length][2];

        for(int i = 0; i < prices.length;i++){
            if(i-1==-1){
                dp[i][0] = 0;
                dp[i][1] = -prices[0];
                continue;
            }
                dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
                dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
            }
        return dp[prices.length-1][0];

    }
}

不同路径LC62 中等

题目链接

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
在这里插入图片描述

示例 1:
输入:m = 3, n = 7
输出:28

示例 2:
输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:
输入:m = 7, n = 3
输出:28

示例 4:
输入:m = 3, n = 3
输出:6

代码:

class Solution {
    public int uniquePaths(int m, int n) {
        int[][] dp = new int[m][n];

        for(int i = 0;i < m; i++){
            for(int j = 0; j < n; j++){
                if(i==0||j==0){
                    dp[i][j] = 1;
                    continue;
                }

                dp[i][j] = dp[i][j-1] + dp[i-1][j];
            }
        }
        return dp[m-1][n-1];
    }

最小路径和LC64 中等

题目链接

给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。
在这里插入图片描述
代码:

class Solution {
    public int minPathSum(int[][] grid) {
        int m = grid.length;
        int n = grid[0].length;

        int[][] dp = new int[m][n];
        // base case 
        dp[0][0] = grid[0][0];
        for(int i = 1;i < m;i++){
            dp[i][0] = dp[i-1][0]+grid[i][0];
        }
        for(int i = 1;i < n;i++){
            dp[0][i] = dp[0][i-1]+grid[0][i];
        }
        
        for(int i = 1;i < m; i++){
            for(int j = 1; j < n; j++){
                dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
            }
        }
        return dp[m-1][n-1];
    }
}

分割等和子集LC416 中等

题目链接

给你一个 只包含正整数 的 非空 数组 nums 。请你判断是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

示例 1:
输入:nums = [1,5,11,5]
输出:true
解释:数组可以分割成 [1, 5, 5] 和 [11] 。

示例 2:
输入:nums = [1,2,3,5]
输出:false
解释:数组不能分割成两个元素和相等的子集。

题解:labuladong的算法笔记

/**

引用:Labuladong《算法小抄》 的第 192 页。

对于这个问题,我们可以先对集合求和,得出 sum,然后把问题转化为背包问题:
给一个可装载重量为 sum / 2 的背包和 N 个物品,每个物品的重量为 nums[i]。现在让你装物品,是否存在一种装法,能够恰好将背包装满?
第一步要明确两点,「状态」和「选择」,状态就是「背包的容量」和「可选择的物品」,选择就是「装进背包」或者「不装进背包」。
dp 数组的定义:dp[i][j] = x 表示,对于前 i 个物品,当前背包的容量为 j 时,若 x 为 true,则说明可以恰好将背包装满,若 x 为 false,则说明不能恰好将背包装满。
根据 dp 数组含义,可以根据「选择」对 dp[i][j] 得到以下状态转移:
如果不把 nums[i] 算入子集,或者说你不把这第 i 个物品装入背包,那么是否能够恰好装满背包,取决于上一个状态 dp[i-1][j],继承之前的结果。
如果把 nums[i] 算入子集,或者说你把这第 i 个物品装入了背包,那么是否能够恰好装满背包,取决于状态 dp[i-1][j-nums[i-1]]
 */

class Solution {
    public boolean canPartition(int[] nums) {
        
        int sum = 0; // 数组一半的大小
        for(int n:nums){
            sum+=n;
        }
        if(sum%2!=0)return false;
        sum = sum/2;

        boolean[][] dp = new boolean[nums.length+1][sum+1];// dp[i][j] 前i个物品,j大小的包,恰好能装满即为true
        // base case
        for(int i = 0;i < nums.length;i++){
            dp[i][0] = true;
        }

        for(int i = 1; i <= nums.length;i++){
            for(int j = 1; j <= sum; j++){
                if(j-nums[i-1]<0){ // 第i个物品装不下
                    dp[i][j] = dp[i-1][j];
                }
                else{ // 能装下
                    dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i-1]];  // 装 or 不装
                }
            }
        }
        return dp[nums.length][sum];
    }
}

参考:
Labuladong
代码随想录
力扣DP题库
牛客面试必刷

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1053117.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【图像分割】图像检测(分割、特征提取)、各种特征(面积等)的测量和过滤(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

基于SSM+Vue的医院住院综合服务管理系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用Vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

前端最新支持四级及以下结构仿企查查、天眼查关联投资机构 股权结构 tree树形结构 控股结构

随着技术的发展&#xff0c;开发的复杂度也越来越高&#xff0c;传统开发方式将一个系统做成了整块应用&#xff0c;经常出现的情况就是一个小小的改动或者一个小功能的增加可能会引起整体逻辑的修改&#xff0c;造成牵一发而动全身。通过组件化开发&#xff0c;可以有效实现单…

数据结构——堆(C语言)

本篇会解决一下几个问题&#xff1a; 1.堆是什么&#xff1f; 2.如何形成一个堆&#xff1f; 3.堆的应用场景 堆是什么&#xff1f; 堆总是一颗完全二叉树堆的某个节点总是不大于或不小于父亲节点 如图&#xff0c;在小堆中&#xff0c;父亲节点总是小于孩子节点的。 如图&a…

外包干了3个月,整个人都萎靡不振了。。。。。

先说一下自己的情况&#xff0c;本科生生&#xff0c;19年通过校招进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测…

基于Java的美容院管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

Android 内存泄漏分析思路和案例剖析

分析思路 内存泄漏是指 Android 进程中&#xff0c;某些对象已经不再使用&#xff0c;但被一些生命周期更长的对象引用&#xff0c;导致其占用的内存资源无法被GC回收&#xff0c;内存占用不断增加的一种现象&#xff1b;内存泄漏是导致我们应用性能下降、卡顿的一种常见因素&…

ROS2 中的轻量级、自动化、受控回放

一、说明 这篇文章描述了一种在 ROS2 中实现受控重播器的轻量级方法。用以测试中将现象重新播放一遍&#xff0c;以实现调参或故障定位的目的。所有源代码都可以在这里找到。该帖子也可在此处获得。 二、问题&#xff1a;不同步重播 任何曾经认真开发过 ROS2 的人都会知道这个问…

microsoft excel 公式 计算本金 利息 月供 和总利息

//可使用 Excel 公式指导算出贷款的月还款额。 PMT(rate, nper, pv, [fv], [type])//返回根据定期固定付款和固定利率而定的投资在已知期间内的本金偿付额。 PPMT(rate, per, nper, pv, [fv], [type])//基于固定利率及等额分期付款方式&#xff0c;返回给定期数内对投资的利息…

C++ Primer----1.5类简介 章节练习

头文件 Sales_item.h #ifndef SALESITEM_H #define SALESITEM_H #include <iostream> #include <string>class Sales_item{ public:Sales_item(const std::string &book):isbn(book),units_sold(0),revenue(0.0){}Sales_item(std::istream &is){ is >&…

在macOS上从源代码构建编译Aseprite到组装成App应用(macOS上如何不开着终端窗口)

这里记录一下自己构建 Aseprite 流程。关于如何构建可能已经有很多文章写了&#xff0c;但是都没有提到一点&#xff1a;启动的时候需要点击可执行程序&#xff0c;这样是需要终端窗口一直开着的的&#xff08;如下图&#xff09;&#xff0c;关闭终端就会退出程序&#xff0c;…

MEIS —— 前端部分基本配置

项目基本配置 这篇文章我们随着上一篇文章继续往下叙述&#xff0c;主要是将element和windicss等开发配置进项目中&#xff0c;以及基本的一些页面和组件给他完成。 1. 安装element plus 运行&#xff1a; npm install element-plus --save 这里我们是按需引入(自动)&#x…

JavaSE学习--数据类型和运算符

&#x1f495;"哪里有人喜欢孤独&#xff0c;只不过更不喜欢失望。"&#x1f495; 作者&#xff1a;Mylvzi 文章主要内容&#xff1a;JJavaSE学习--数据类型和运算符 Java程序是如何运行的&#xff1f; 一.数据类型 整型&#xff1a; 注意事项&#xff1a; 1.整型…

Android 使用Kotlin封装RecyclerView

文章目录 1.概述2.运行效果图3.代码实现3.1 扩展RecyclerView 3.2 扩展Adapter3.3 RecyclerView装饰绘制3.3.1 以图片实现分割线3.3.2 画网格线3.3.3空白的分割线3.3.4 不同方向上的分割线 3.4 使用方法 1.概述 在一个开源项目上看到了一个Android Kotlin版的RecyclerView封装…

Linux 网络编程

套接字&#xff08;Socket&#xff09;&#xff1a; 通过网络实现跨机通信 作用&#xff1a;一种文件描述符传输层的文件描述符 整个编程中&#xff0c;需要着重注意htonl/htons、ntohl/ntohs、inet_addr等 TCP的C/S实现 循环服务器模型 TCP服务器实现过程 1.创建套接字&a…

Web 中间件怎么玩?

本次主要是聊聊关于 web 中间件&#xff0c; 分为如下四个方面 什么是 web 框架中间件 为什么要使用 web 中间件 如何使用及其原理 哪些场景需要使用中间件 开门见山 web 中间件是啥 Web 框架中的中间件主要指的是在 web 请求到具体路由之前或者之后&#xff0c;会经过一个或…

MyBatis 映射文件(Mapper XML):配置与使用

MyBatis 映射文件&#xff08;Mapper XML&#xff09;&#xff1a;配置与使用 MyBatis是一个强大的Java持久化框架&#xff0c;它允许您将SQL查询、插入、更新和删除等操作与Java方法进行映射。这种映射是通过MyBatis的映射文件&#xff0c;通常称为Mapper XML文件来实现的。本…

正点原子lwIP学习笔记——MQTT协议

1. MQTT简介 MQTT是一种基于客户端服务端架构的发布/订阅模式的消息传输协议。他的设计思想是轻巧、开放、简单、规范&#xff0c;易于实现。这些特点使得他对很多场景来说都是很好的选择&#xff0c;尤其是对于受限的环境如机器与机器的通信&#xff08;M2M&#xff09;以及物…

python根据命令行参数动态导入模块或文件

需求 在命令行运行一个 python 文件&#xff0c;同时传入自定义参数&#xff1a; $ python main.py --nodeTable --actioncreate --data"{name: test2, is_sys_obj: False, encoding: UTF8,datconnlimit: -1, variables: []"希望 main.py 接收命令行参数&#xff0…

1.6.C++项目:仿mudou库实现并发服务器之channel模块的设计

项目完整版在&#xff1a; 文章目录 一、channel模块&#xff1a;事件管理Channel类实现二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、代码&#xff08;一&#xff09;框架&#xff08;二…