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

文章目录

      • 跳台阶 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);// 注意填充-1return 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);// 注意填充-1int r1 =  DP(nums,0,len-2,0);Arrays.fill(memory,-1);// 注意填充-1int 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];//baseif(i>end)return 0;//baseif(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;// 两个数组,就是两个forfor(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 hereif(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-1DP(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/不持有0for(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 casefor(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.mzph.cn/news/91437.shtml

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

相关文章

C++ AB组辅导课

C AB组辅导课 蓝桥杯C AB组辅导课 第一讲 递归与递推 Acwing1、整数划分(递归)2、acwing92. 递归实现指数型枚举10凑算式(全排列)11李白打酒(全排列)12、棋牌总数(递归)13、剪邮票(递归)14、1050. 鸣人的影分身 (递归或动态规划(记忆化搜索))15、方格分割 &#xff08;dfs思维&…

分类预测 | Matlab实现BES-ELM秃鹰搜索算法优化极限学习机分类预测

分类预测 | Matlab实现BES-ELM秃鹰搜索算法优化极限学习机分类预测 目录 分类预测 | Matlab实现BES-ELM秃鹰搜索算法优化极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 Matlab实现BES-ELM秃鹰搜索算法优化极限学习机分类预测&#xff08;完整源码和数…

一篇文章教你自动化测试如何解析excel文件?

前言 自动化测试中我们存放数据无非是使用文件或者数据库&#xff0c;那么文件可以是csv&#xff0c;xlsx&#xff0c;xml&#xff0c;甚至是txt文件&#xff0c;通常excel文件往往是我们的首选&#xff0c;无论是编写测试用例还是存放测试数据&#xff0c;excel都是很方便的。…

【Arduino ESP32教程入门】Note

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;那个传说中的man的主页 &#x1f3e0;个人专栏&#xff1a;题目解析 &#x1f30e;推荐文章&#xff1a;题目大解析3 目录 &#x1f449;&#x1f3fb;arduino开发板引脚USB串行总线串行总线和并行总线的优…

HDFS编程实践-从HDFS中下载指定文件到本地

前言&#xff1a;Hadoop采用java语言开发&#xff0c;提供了Java Api与HDFS进行交互 先要把hadoop的jar包导入到idea中去 为了能编写一个与hdfs交互的java应用程序&#xff0c;一般需要向java工程中添加以下jar包 1&#xff09;/usr/local/hadoop/share/hadoop/common目录下…

C++(string类)

本节目标&#xff1a; 1、为什么要学习string类 2.标准库中的string类 3.vs和g下string结构说明 1.为什么学习string类 1.1 c语言中的字符串 C 语言中&#xff0c;字符串是以 \0 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c; C 标准库中提供了一些 str系列的…

Python 内置函数详解 (3) 进制转换

近期在外旅游,本篇是出发前定时发布的,不完整,旅游回来后再补充。 Python 内置函数 Python3.11共有75个内置函数,其来历和分类请参考:Python 新版本有75个内置函数,你不会不知道吧_Hann Yang的博客-CSDN博客 函数列表 abs aiter all …

Android LiveData 介绍

Android LiveData 介绍 系列文章目录前言一、LiveData是什么&#xff1f;二、简单使用依赖测试数据准备1.创建可观察的livedata2.观察它3.更新它 总结 系列文章目录 Android LiveData 介绍&#xff08;本文&#xff09; 前言 本系列根据官网介绍Jetpack中的数据通信组件&…

Appium开发

特点 开源免费支持多个平台 IOS(苹果)、安卓App的自动化都支持 支持多种类型的自动化 支持苹果、安卓应用原生界面的自动化支持应用内嵌网络视图的自动化支持手机浏览器(Chrome)中的web网站自动化支持flutter应用的自动化 支持多种编程语言 像selenium一样&#xff0c;可以用多…

27、Flink 的SQL之SELECT (Pattern Recognition 模式检测)介绍及详细示例(7)

Flink 系列文章 1、Flink 部署、概念介绍、source、transformation、sink使用示例、四大基石介绍和示例等系列综合文章链接 13、Flink 的table api与sql的基本概念、通用api介绍及入门示例 14、Flink 的table api与sql之数据类型: 内置数据类型以及它们的属性 15、Flink 的ta…

最新AI智能创作系统ChatGPT商业源码+详细图文搭建部署教程+AI绘画系统

一、AI系统介绍 SparkAi创作系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&am…

机器学习之广义增量规则(Generalized Delta Rule)

文章目录 广义增量规则的公式s型函数的增量规则 广义增量规则的公式 对于单层神经网络的增量规则&#xff0c;已经过时啦&#xff0c;现在存在一种更广义的增量规则形式。对于任意激活函数&#xff0c;增量规则表示如下式它与前一节的delta规则相同&#xff0c;只是ei被替换为…

ccf_csp第一题汇总

ccf_csp第一题汇总 printf()输出格式大全&#xff08;附 - 示例代码&#xff09;现值计算AcWing 4699. 如此编码AcWing 4509. 归一化处理(小数位数根号函数)AcWing 4454. 未初始化警告AcWing 4280. 序列查询AcWing 4006. 数组推导(小陷阱)AcWing 3292. 称检测点查询AcWing 3287…

Linux知识点 -- 网络基础 -- 传输层

Linux知识点 – 网络基础 – 传输层 文章目录 Linux知识点 -- 网络基础 -- 传输层一、传输层协议1.端口号2.网络相关bash命令 二、UDP协议1.UDP报文的解包与交付2.理解UDP报文3.UDP协议的特点4.UDP应用层IO类接口5.UDP的缓冲区6.UDP使用注意事项7.基于UDP的应用层协议 三、TCP协…

lv5 嵌入式开发-11 消息队列

掌握&#xff1a;消息队列机制、打开/创建消息队列、发送消息、接收消息 1 消息队列 消息队列是System V IPC对象的一种 消息队列由消息队列ID来唯一标识 消息队列就是一个消息的列表。用户可以在消息队列中添加消息、读取消息等 消息队列可以按照类型来发送/接收消息 消…

Spring Boot:利用JPA进行数据库的增改

目录 JPA介绍Service接口Service和Autowired示例代码 Dao数据库操作层Repository示例代码 控制器文件示例代码-增加增加成功示例代码-修改修改成功 JPA介绍 JPA&#xff08;Javaa Persistence API)一种用于持久化 Java 对象到关系型数据库的标准规范。它提供了一种统一的方式来…

Pytorch单机多卡分布式训练

Pytorch单机多卡分布式训练 数据并行&#xff1a; DP和DDP 这两个都是pytorch下实现多GPU训练的库&#xff0c;DP是pytorch以前实现的库&#xff0c;现在官方更推荐使用DDP&#xff0c;即使是单机训练也比DP快。 DataParallel&#xff08;DP&#xff09; 只支持单进程多线程…

openGauss学习笔记-83 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用内存和存储规划

文章目录 openGauss学习笔记-83 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用内存和存储规划83.1 MOT内存规划83.2 存储IO83.3 容量需求 openGauss学习笔记-83 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用内存和存储规划 本节描述了为满足特定…

完整的 pixel 6a 刷入 AOSP 源码过程记录

基础环境 虚拟机&#xff1a;VMware Workstation 16 Pro 16.0.0 build-16894299 Linux版本&#xff1a;ubuntu-16.04.7-desktop-amd64 设备&#xff1a;pixel 6a&#xff1b;代号&#xff1a;bluejay&#xff1b; 基础软件安装 安装 Git 命令&#xff1a;sudo apt install git …

金融生产存储亚健康治理:升级亚健康 3.0 ,应对万盘规模的挑战

随着集群规模的不断扩大&#xff0c;硬盘数量指数级上升&#xff0c;信创 CPU 和操作系统、硬盘多年老化、物理搬迁等多种复杂因素叠加&#xff0c;为企业的存储亚健康管理增加了新的挑战。 在亚健康 2.0 的基础上&#xff0c;星辰天合在 XSKY SDS V6.2 实现了亚健康 3.0&#…