Leetcode416/1049/494之01揹包型別的例題
阿新 • • 發佈:2022-04-18
Leetcode416-分割等和子集
- 給你一個 只包含正整數 的 非空 陣列
nums
。請你判斷是否可以將這個陣列分割成兩個子集,使得兩個子集的元素和相等。 - 輸入:nums = [1,5,11,5]
- 輸出:true
//二維陣列版 public boolean canPartition(int[] nums) { int sum = 0; int maxNum = Integer.MIN_VALUE; for (int i = 0; i < nums.length; i++) { sum += nums[i]; maxNum = Math.max(maxNum, nums[i]); } if (sum % 2 == 1 || nums.length == 1 || maxNum > sum / 2) { return false; } int target = sum / 2; boolean[][] dp = new boolean[nums.length][target + 1]; for (int i = 0; i < nums.length; i++) { dp[i][0] = true; } dp[0][nums[0]] = true;//其餘都預設false for (int i = 1; i < nums.length; i++) { int num = nums[i]; for (int j = 1; j <= target; j++) { if (j >= num) { dp[i][j] = dp[i - 1][j] | dp[i - 1][j - num]; } else { dp[i][j] = dp[i - 1][j]; } } } return dp[nums.length - 1][target]; }
//一維陣列版 public boolean canPartition2(int[] nums) { int sum = 0; int maxNum = Integer.MIN_VALUE; for (int i = 0; i < nums.length; i++) { sum += nums[i]; maxNum = Math.max(maxNum, nums[i]); } //先進行一遍篩選 if (sum % 2 == 1 || nums.length == 1 || maxNum > sum / 2) { return false; } int target = sum / 2; //定義dp陣列 boolean[] dp = new boolean[target + 1]; //初始化 dp[0] = true; //遍歷 for (int i = 0; i < nums.length; i++) { //因為是01揹包問題 所以此處倒序 for (int j = target; j >= nums[i]; j--) { dp[j] = dp[j] || dp[j - nums[i]]; } } return dp[target]; }
Leetcode1049-最後一塊石頭的重量二
- 有一堆石頭,用整數陣列
stones
表示。其中stones[i]
表示第i
塊石頭的重量。 - 每一回合,從中選出任意兩塊石頭,然後將它們一起粉碎。假設石頭的重量分別為
x
和y
,且x <= y
。那麼粉碎的可能結果如下:- 如果
x == y
,那麼兩塊石頭都會被完全粉碎; - 如果
x != y
,那麼重量為x
的石頭將會完全粉碎,而重量為y
的石頭新重量為y-x
。
- 如果
- 最後,最多隻會剩下一塊 石頭。返回此石頭 最小的可能重量 。如果沒有石頭剩下,就返回
0
。 - 輸入:stones = [2,7,4,1,8,1]
- 輸出:1
//二維陣列版 public int lastStoneWeightII(int[] stones) { int totalWeight = 0; for (int i = 0; i < stones.length; i++) { totalWeight += stones[i]; } int targetSum = totalWeight / 2; int[][] dp = new int[stones.length][targetSum + 1]; for (int i = 0; i <= targetSum; i++) { if (i >= stones[0]) { dp[0][i] = stones[0]; } } for (int i = 1; i < stones.length; i++) { int tempStone = stones[i]; for (int j = 1; j <= targetSum; j++) { if (j >= tempStone) { dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - tempStone] + tempStone); } else { dp[i][j] = dp[i - 1][j]; } } } int restSum = totalWeight - dp[stones.length - 1][targetSum]; return restSum - dp[stones.length - 1][targetSum]; }
//一維陣列版
public int lastStoneWeightII2(int[] stones) {
int totalWeight = 0;
for (int i = 0; i < stones.length; i++) {
totalWeight += stones[i];
}
int targetSum = totalWeight / 2;
int[] dp = new int[targetSum + 1];
for (int i = 0; i < stones.length; i++) {
for (int j = targetSum; j >= stones[i]; j--) {
dp[j] = Math.max(dp[j], dp[j - stones[i]] + stones[i]);
}
}
return totalWeight - 2 * dp[targetSum];
}
Leetcode494-目標和
- 給你一個整數陣列
nums
和一個整數target
。 - 向陣列中的每個整數前新增
'+'
或'-'
,然後串聯起所有整數,可以構造一個 表示式 :- 例如,
nums = [2, 1]
,可以在2
之前新增'+'
,在1
之前新增'-'
,然後串聯起來得到表示式"+2-1"
。
- 例如,
- 返回可以通過上述方法構造的、運算結果等於
target
的不同 表示式 的數目。 - 輸入:nums = [1,1,1,1,1], target = 3
- 輸出:5
//遞迴法
int res=0;
public int findTargetSumWays(int[] nums, int target) {
getSum(nums,target,0,0);
return res;
}
public void getSum(int[] nums,int target,int sum,int startIndex){
if(startIndex==nums.length){
if(sum==target){
res++;
}
return;
}
sum+=nums[startIndex];
getSum(nums,target,sum,startIndex+1);
sum-=nums[startIndex];
sum-=nums[startIndex];
getSum(nums,target,sum,startIndex+1);
sum+=nums[startIndex];
}
@Test
public void test(){
int[] nums=new int[]{1,1,1};
int target=1;
int targetSumWays = findTargetSumWays(nums, target);
System.out.println(targetSumWays);
}
//動態規劃法
//假設加法的總和為x,那麼減法對應的總和就是sum - x。
//所以我們要求的是 x - (sum - x) = S 即x = (S + sum) / 2
//此時問題就轉化為,裝滿容量為x揹包,有幾種方法。
//此處還是排列問題 所以遞迴式為dp[j]+=dp[j-nums[i]];
public int findTargetSumWays2(int[] nums, int target) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum+=nums[i];
}
if(target>sum || (target*-1)>sum){
return 0;
}
if((target+sum)%2==1){
return 0;
}
int tt=(target+sum)/2;
int[] dp=new int[tt+1];
dp[0]=1;
for(int i=0;i<nums.length;i++){
for(int j=tt;j>=nums[i];j--){
dp[j]+=dp[j-nums[i]];
}
}
return dp[tt];
}