日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【LeetCode笔记】416. 分割等和子集(Java、动态规划、背包问题、滚动数组)

發布時間:2024/7/23 java 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【LeetCode笔记】416. 分割等和子集(Java、动态规划、背包问题、滚动数组) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 題目描述
  • 思路 && 代碼
      • 1. 動態規劃 O(nc) 、O(nc)
      • 2. 結合滾動數組 O(nc)、O(c)
      • 二刷

打卡第十四天~熬夜也得把題目補上= =

題目描述

  • 初看題目,想到的思路是用記憶化DFS來找結果來著。。看了題解才知道是背包問題= =

思路 && 代碼

1. 動態規劃 O(nc) 、O(nc)

  • 參考了liweiwei的這篇題解,里面給背包問題講了一些相關知識~
  • 時空復雜度: n 是 nums 的長度,c 是 sum 的長度。
  • dp[i][j]:從[0, i]下標組成的數集里選取元素相加,能否構成 j 。
  • 狀態轉移方程:【[0, i - 1] 可以構成 j】| 【[0, i - 1] 可以構成 j - nums[i]】,滿足任一種情況,都可以保證 dp[i][j] == true,因此這里采用 |=
class Solution {public boolean canPartition(int[] nums) {// 題意轉化:找到一個集合,滿足其和等于 nums 總和的一半 sum / 2int sum = 0;for(int num : nums) {sum += num;}// 奇數,肯定無法滿足 sum / 2if(sum % 2 == 1) {return false;}// [0 ~ i 范圍的元素][背包容量(包括0)]int target = sum / 2;boolean[][] dp = new boolean[nums.length][target + 1];// 1. 邊界:第一個元素,只能滿足對應的容量if(nums[0] <= target) {dp[0][nums[0]] = true;}// 2. 狀態轉移for(int i = 1; i < nums.length; i++) {for(int j = 0; j <= target; j++) {// part 1: 先把上一輪的結果繼承下來再說dp[i][j] = dp[i - 1][j];// part 2: 狀態轉移方程:看[0, i - 1]能不能滿足 j - nums[i],再補上 nums[j]if(nums[i] <= j) {dp[i][j] |= dp[i - 1][j - nums[i]];}}}return dp[nums.length - 1][target];} }

2. 結合滾動數組 O(nc)、O?

  • 在1的基礎上,通過滾動數組來減少空間復雜度。在劍指Offer 47 禮物的最大值里,也有用到這個方法~。
  • 加入 if(dp[target]) 的判斷實現剪枝效果,可以打敗98%+,這里為了看起來簡潔就不加上了
  • 注意:逆序是為了達到無后效性的效果。如果正序,會導致后面的列用到的不是上一行的結果,而是當前行的結果,會導致出錯(可以畫圖理解一下,或者看上面1提到的題解的解釋)
class Solution {public boolean canPartition(int[] nums) {// 題意轉化:找到一個集合,滿足其和等于 nums 總和的一半 sum / 2int sum = 0;for(int num : nums) {sum += num;}// 奇數,肯定無法滿足 sum / 2if(sum % 2 == 1) {return false;}// [0 ~ i 范圍的元素][背包容量(包括0)]int target = sum / 2;boolean[] dp = new boolean[target + 1];// 1. 邊界:第一個元素,只能滿足對應的容量if(nums[0] <= target) {dp[nums[0]] = true;}// 2. 狀態轉移for(int i = 1; i < nums.length; i++) {// 逆序,達到無后效性的效果for(int j = target; j >= 0; j--) {// part 1: 先把上一輪的結果繼承下來再說(滾動數組不用考慮)// part 2: 狀態轉移方程:看[0, i - 1]能不能滿足 j - nums[i],再補上 nums[j]if(nums[i] <= j) {dp[j] |= dp[j - nums[i]];}}}return dp[target];} }

二刷

背包!
你就說你選不選吧(指元素)
你要能 true 我肯定選啊

  • O(nc)、O(nc)
class Solution {public boolean canPartition(int[] nums) {int sum = 0;for(int num : nums) {sum += num;}if((sum & 1) == 1) {return false;}// dp[i][j]:從 [0, i] 的下標中,能找到和為 j 的值int target = sum / 2;boolean[][] dp = new boolean[nums.length][target + 1];if(nums[0] <= target) {dp[0][nums[0]] = true;}for(int i = 1; i < nums.length; i++) {for(int j = 0; j <= target; j++) {// 繼承結果 | 當前可行dp[i][j] = (dp[i - 1][j]) | (nums[i] <= j ? dp[i - 1][j - nums[i]] : false);}}return dp[nums.length - 1][target];} }
  • 滾動數組:逆序降低空間復雜度
class Solution {public boolean canPartition(int[] nums) {int sum = 0;for(int num : nums) {sum += num;}if((sum & 1) == 1) {return false;}int target = sum / 2;boolean[] dp = new boolean[target + 1];if(nums[0] <= target) {dp[nums[0]] = true;}for(int i = 1; i < nums.length; i++) {for(int j = target; j >= 0; j--) {dp[j] |= (nums[i] <= j ? dp[j - nums[i]] : false);}}return dp[target];} }

總結

以上是生活随笔為你收集整理的【LeetCode笔记】416. 分割等和子集(Java、动态规划、背包问题、滚动数组)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。