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

文章目录

      • 跳台阶 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.xdnf.cn/news/146576.html

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

相关文章

QT的ui设计中改变样式表的用法

在QT的ui设计中,我们右键会弹出一个改变样式表的选项,很多人不知道这个是干什么的。 首先我们来看下具体的界面 首先我们说一下这个功能具体是干嘛的, 我们在设置很多控件在界面上之后,常常都是使用系统默认的样式,但是当有些时候为了美化界面我们需要对一些控件进行美化…

【数据结构】链表与LinkedList

作者主页&#xff1a;paper jie 的博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文录入于《JAVA数据结构》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精…

小程序编译器性能优化之路

作者 | 马可 导读 小程序编译器是百度开发者工具中的编译构建模块&#xff0c;用来将小程序代码转换成运行时代码。旧版编译器由于业务发展&#xff0c;存在编译慢、内存占用高的问题&#xff0c;我们对编译器做了一次大规模的重构&#xff0c;采用自研架构&#xff0c;做了多线…

链表经典面试题(一)

面试题 1.反转链表的题目2.反转链表的图文分析3.反转链表的代码实现 1.反转链表的题目 2.反转链表的图文分析 我们在实现反转链表的时候,是将后面的元素变前面&#xff0c;前面的元素变后面&#xff0c;那么我们是否可以理解为&#xff0c;用头插法的思想来完成反转链表呢&…

Cannot download sources:IDEA源码无法下载

问题 Swagger的相关包&#xff0c;无法看到注释&#xff1b; 在class文件的页面&#xff0c;点击下载源码&#xff0c;源码下载不了&#xff0c;IDEA报下面的错误。 报错 Cannot download sources Sources not found for: io.swagger.core.v3:swagger-annotations:2.2.9 解决…

读者写者问题—内含408真题

读者写者问题—含408 一、问题描述 一个数据问价或记录可以被多个进程共享&#xff0c;我们把只读该文件的进程称为“读者进程”&#xff0c;其他进程为“写者进程”。允许多个进程同时读一个共享对象&#xff0c;但不允许一个写者进程和其他写者进程或读者进程同时访问共享对…

点亮一个LED+LED闪烁+LED流水灯——“51单片机”

各位CSDN的uu们好呀&#xff0c;这是小雅兰的最新专栏噢&#xff0c;最近小雅兰学习了51单片机的知识&#xff0c;所以就想迫不及待地分享出来呢&#xff01;&#xff01;&#xff01;下面&#xff0c;让我们进入51单片机的世界吧&#xff01;&#xff01;&#xff01; 点亮一个…

Linux基础命令汇总

用户管理 su 切换用户&#xff1a;su 用户名 logname 显示当前用户的登录用户名&#xff1a;logname useradd 创建用户&#xff1a;useradd 用户名创建用户时指定用户的主组&#xff1a;useradd -g 组名 用户名 usermod 添加附属组&#xff1a;usermod -G 组…

2023年8月嵌入式项目开发专题总汇

一、前言 本文介绍基于嵌入式系统和C语言开发的系列项目。这些项目涵盖了多个领域&#xff0c;从自动化控制到游戏开发&#xff0c;从计算机网络到物联网应用。通过这些项目的开发过程&#xff0c;将深入探讨各种技术和解决方案&#xff0c;并分享相关经验和知识。 在本文中&…

cesium 雷达扫描 (线行扩散效果)

cesium 雷达扫描 (线行扩散效果) 1、实现方法 使用ellipse方法加载圆型,修改ellipse中material方法来实现效果 2、示例代码 2.1、 <!DOCTYPE html> <html lang="en"><head><<

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

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

【CVPR 2023】DSVT: Dynamic Sparse Voxel Transformer with Rotated Sets

文章目录 开场白效果意图 重点VoxelNet: End-to-End Learning for Point Cloud Based 3D Object DetectionX-Axis DSVT LayerY-Axis DSVT Layer Dynamic Sparse Window AttentionDynamic set partitionRotated set attention for intra-window feature propagation.Hybrid wind…

优维低代码实践:应用级配置

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

[NOIP2012 提高组] 开车旅行

[NOIP2012 提高组] 开车旅行 题目描述 小 A \text{A} A 和小 B \text{B} B 决定利用假期外出旅行&#xff0c;他们将想去的城市从 $1 $ 到 n n n 编号&#xff0c;且编号较小的城市在编号较大的城市的西边&#xff0c;已知各个城市的海拔高度互不相同&#xff0c;记城市 …

亚信科技AntDB数据库 高并发、低延迟、无死锁,深入了解AntDB-M元数据锁的实现

AntDB-M在架构上分为两层&#xff0c;服务层和存储引擎层。元数据的并发管理集中在服务层&#xff0c;数据的存储访问在存储引擎层。为了保证DDL操作与DML操作之间的一致性&#xff0c;引入了元数据锁&#xff08;MDL&#xff09;。 AntDB-M提供了丰富的元数据锁功能&#xff0…

Koa处理请求数据

在开发中&#xff0c;后端接收到请求参数后&#xff0c;需要解析参数。请求分为很多种类型&#xff0c;比如常见的get和post。 请求参数 Koa本身可以解析get请求参数&#xff0c;不能解析post请求参数。例如&#xff1a; router.get(/api/get/userInfo, async (context) >…

新手--安装好Quartus II13.0(带modelsim集成包)并用Quartus II搭建一个工程

前言 今天是国庆节&#xff0c;我们正式来学习Quartus II13.0软件的安装与使用。学习verilog与学习C语言都是学习一门语言&#xff0c;那么学习一门语言&#xff0c;光看理论不敲代码绝对是学习不好的。要用verilog语言敲代码&#xff0c;就要像C语言那样搭建起语言的编译环境&…

C语言 Cortex-A7核 IIC实验

iic.h #ifndef __IIC_H__ #define __IIC_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h" /* 通过程序模拟实现I2C总线的时序和协议* GPIOF ---> AHB4* I2C1_SCL ---> PF14* I2C1_SDA ---> PF15** */#define SET_SDA_OUT do{…

B. Comparison String

题目&#xff1a; 样例&#xff1a; 输入 4 4 <<>> 4 >><< 5 >>>>> 7 <><><><输出 3 3 6 2 思路&#xff1a; 由题意&#xff0c;条件是 又因为要使用尽可能少的数字&#xff0c;这是一道贪心题&#xff0c;所以…

Linux CentOS7 vim临时文件

在vim中&#xff0c;由于断网、停电、故意退出、不小心关闭终端等多种原因&#xff0c;正在编辑的文件没有保存&#xff0c;系统将会为文件保存一个交换文件&#xff0c;或称临时文件&#xff0c;或备份文件。 如果因某种原因产生了交换文件&#xff0c;每次打开文件时&#x…