今天做了一道背包问题的变种问题,这个问题还是用动态规划来做,但是做法上跟原来的背包问题有很大的区别。
题意:
给出一个都是正整数的数组 nums,其中没有重复的数。从中找出所有的和为 target 的组合个数。
样例:
给出 nums = [1, 2, 4], target = 4 可能的所有组合有: [1, 1, 1, 1] [1, 1, 2] [1, 2, 1] [2, 1, 1] [2, 2] [4] 返回 6
1.最简单的方法--深搜(超时)
看到这种问题,特别是要求我们将所有的情况计算出来,我们首先想到的是就是深搜。这个题用深搜做时非常的简单,但是不可避免的就是超时。
代码:
1 private static int count = 0; 2 3 public static int backPackVI(int[] nums, int target) { 4 dfs(nums, new int[target], 0, 0, target); 5 return count; 6 } 7 /** 8 * 9 * @param nums 原来的数组 10 * @param select 选择的数字组成的数组,为什么它的长度为target,那么因为要想和等于target,最长的情况是全部是1 11 * 所以最长为target 12 * @param i 记录开始填充select数组里面的第i个位置了 13 * @param sum //select数组的和 14 * @param target //目标值 15 */ 16 private static void dfs(int nums[], int select[], int i, int sum, int target) { 17 if (sum == target) { 18 count++; 19 } else { 20 //这里一定要记住i必须小于select的长度 21 for (int j = 0; j < nums.length && i < select.length; j++) { 22 if (sum + nums[j] <= target) { 23 select[i] = nums[j]; 24 dfs(nums, select, i + 1, sum + nums[j], target); 25 } 26 27 } 28 } 29 }
2.动态规划
动态规划第一步的操作就是填表,所以,我们要想推导出动态规划的方程,必须先填表(填表的假设条件的设置尤为重要)。如图所示:
代码:
1 public static int backPackVI(int[] nums, int target) { 2 //dp数组,用来记录在每一种情况下,不同数字装的所有情况个数 3 int dp[][] = new int[target + 1][nums.length]; 4 //不同target下,所有情况的个数 5 int b[] = new int[target+ 1]; 6 for(int i = 0; i < dp.length; i++){ 7 Arrays.fill(dp[i], 0); 8 } 9 Arrays.fill(b, 0); 10 for(int i = 1; i < dp.length; i++) 11 { 12 for(int j = 0; j < nums.length; j++){ 13 //当当前的数字大于当前的target,肯定不能装进去 14 if(nums[j] > i){ 15 continue; 16 } 17 //dp方程 18 dp[i][j] = b[i - nums[j]]; 19 //当当前的数字恰好等于target时,将dp的值更新,正确值应该为1,先前为0 20 if(nums[j] == i){ 21 dp[i][j] = 1; 22 } 23 } 24 //初始化一下b[i],理论上可以不用初始化,为了保证正确性 25 b[i] = 0; 26 //更新b[i]的值 27 for(int j = 0; j < nums.length; j++){ 28 b[i] += dp[i][j]; 29 } 30 } 31 int sum = 0; 32 for(int i = 0; i < nums.length; i++){ 33 sum += dp[target][i]; 34 } 35 return sum; 36 }
时间: 2024-10-21 00:29:13