【问题标题】:Finding Subsets of Numbers with a Given Sum查找具有给定总和的数字子集
【发布时间】:2016-10-31 18:54:28
【问题描述】:

使用递归,编写一个给定整数列表和给定总和的程序, 将找到总数为给定总和的数字的所有子集。计算找到的子集数。如果不存在子集,则应指出未找到解决方案。例如,给定列表 6、13、3、3,总和为 19,您的程序应该找到两个 解决方案:

6 + 13 = 19
13 + 3 + 3 = 19

将输入列表中的整数个数限制为最多 20 个整数。接受 仅正整数并使用 0 来标记列表的结尾。 以下是运行示例:

Enter positive integers terminated with 0: 6 13 3 3 0
Enter the desired sum: 19
Solution 1:6 +13 =19
Solution 2: 13 +3 +3 =19
Found 2 solutions

这是我的代码,但它只找到一个子集,我想找到所有子集。有什么帮助吗?

 public static boolean SubSetSum(int start, int[] nums, int target) {
        if (start >= nums.length) {
            return (target == 0);
        }
        if (SubSetSum(start + 1, nums, target - nums[start])) {

            System.out.println( nums[start] );
            return true;
        }
        if (SubSetSum(start + 1, nums, target)) {

            return true;
        }
        return false;

    }




    public static void main(String[] args) {

        int[] mySet = {4,1,3,2};
        int sum = 5;
        System.out.println("The Goal is : " + sum);

       SubSetSum(0,mySet, sum) ;
    }
}

【问题讨论】:

  • 作为调试提示,不要直接在 if 条件中调用方法,而是创建一个布尔值,它是 SubSetSum() 调用的结果。
  • 好吧,看看你的代码。如果target==0any 点,那么您已经达到目标,但您没有相应的代码。您仅在 start 位于数组末尾时检查 target==0 是否。所以乍一看,这是最明显的问题。另一个调试提示是在函数的第一行打印出函数参数,这样你就可以跟踪所有的调用。
  • @ItamarGreen SO 的问题指南(您知道,您通常在 cmets 中抱怨的那些)是:准确的问题描述、所做的努力、可编译的示例、实际和预期的输出。似乎它符合我的所有标准。
  • @ItamarGreen 不管硬件与否。这个问题很好。请重新考虑。
  • @NikolasCharalambidis,你说得对。我误解了这个问题,但重读后,我发现我错了

标签: java recursion sum


【解决方案1】:

您的主要问题是:

  • 一旦找到解决方案就会立即返回
  • 您的解决方案只考虑从左到右的数字

您需要为您的解决方案考虑原始列表的所有可能子列表,例如,对于[A, B, C, D] 的列表,解决方案可能是[A, C, D]。因此,一个好的起点是一些能够创建列表的所有子列表的代码。为此,您需要有一组列表,您可以在其中汇总所有可能性。这是一个通过从原始列表的副本中删除元素来实现此目的的示例,但是有很多方法可以做到这一点:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class ResursionTest {
    public static void findSubsets(Set<List<Integer>> allSubsets, List<Integer> nums) {
        if (nums.size() == 0) {
            return;
        }

        // add the current list as a possibility
        allSubsets.add(new ArrayList<>(nums));

        // then add a possibility that has one less
        for (int i = 0; i < nums.size(); i++) {
            final List<Integer> subset = new ArrayList<>(nums);
            subset.remove(i);
            findSubsets(allSubsets, subset);
        }
    }


    public static void main(String[] args) {
        final Integer[] array = {4, 1, 3, 2};
        final HashSet<List<Integer>> allSubsets = new HashSet<>();

        findSubsets(allSubsets, Arrays.asList(array));
        System.out.println(allSubsets);
    }
}

如果您运行此程序,您将通过输出看到我们正在查找原始输入列表 [4, 1, 3, 2] 的所有子列表。

输出检查:

[[3, 2], [1], [4, 3], [2], [3], [1, 2], [4, 3, 2], [1, 3], [4], [4, 1, 2], [4, 1, 3], [4, 1, 3, 2], [4, 1], [1, 3, 2], [4, 2]]

那么剩下的就是只添加与请求数量相加的子列表,而不是添加所有可能性。

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

class ResursionTest {
    public static void findSubsets(Set<List<Integer>> allSubsets, List<Integer> nums, int sum) {
        if (nums.size() == 0) {
            return;
        }

        int currentSum = 0;
        for (Integer num : nums) {
            currentSum += num;
        }

        // does the current list add up to the needed sum?
        if (currentSum == sum) {
            allSubsets.add(new ArrayList<>(nums));
        }

        for (int i = 0; i < nums.size(); i++) {
            final List<Integer> subset = new ArrayList<>(nums);
            subset.remove(i);
            findSubsets(allSubsets, subset, sum);
        }
    }


    public static void main(String[] args) {
        int sum = 5;
        final Integer[] array = {4, 1, 3, 2};
        final HashSet<List<Integer>> allSubsets = new HashSet<>();

        findSubsets(allSubsets, Arrays.asList(array), sum);
        System.out.println(allSubsets);
    }
}

答案检查:

[[3, 2], [4, 1]]

您仍然可以使用此代码进行一些优化,我将留给您。

【讨论】:

  • 感谢您的回复!但是在您编写的代码中,如果您在数组中输入超过 13 个整数,则代码将不起作用
  • 你试过用 13 个整数运行这个程序吗?花了多长时间?
【解决方案2】:

基本问题是您返回了错误的信息:您需要找到的所有解决方案,但根据您是否成功,您只返回了一个布尔值。相反,您需要构建一个解决方案列表。

基本情况:

  • 如果目标 = 0,您成功了。打印解决方案并增加计数器。
  • else if target
  • 否则,如果您没有剩余物品,则失败。

递归案例:

你在基本想法上做得很好:在减去和不减去当前数字的情况下重复。但是,您也可以传递此分支中使用的数字列表——这样,当您到达底部时,您可以打印该列表。还有其他方法可以处理此步骤,但我认为这对您的应用程序来说是最简单的。如果您不允许更改 SubSetSum 的签名,则将其用作“包装器”并从那里调用您的真实函数,从一个空列表开始。

这会让你动起来吗?你也可以查看之前关于这个问题的几十个问题。

【讨论】:

    【解决方案3】:

    我的 Dp 解决方案:-

    public static void main(String[] args) {
            Scanner sc = new Scanner(System.in);
            int t = sc.nextInt();
            while(t-->0){
                int n = sc.nextInt();
            int arr[] = new int[n];
            for(int i = 0;i<n;i++){
                arr[i] = sc.nextInt();
            }
            int target = sc.nextInt();
            int dp[] = new int[target+1];
            dp[0] = 1;
            int currSum = 0;
            for(int i = 0;i<n;i++){
                currSum += arr[i];
                for(int j =  Math.min(currSum,target);j>= arr[i];j--){
                    dp[j] += dp[j-arr[i]];
                }
            }
            System.out.println(dp[target]);
            }
        }
    

    时间复杂度。 O(N*target) 空间复杂度。 O(N)

    【讨论】:

      猜你喜欢
      • 2017-01-12
      • 1970-01-01
      • 1970-01-01
      • 2014-11-15
      • 2016-04-03
      • 2017-01-12
      • 2012-12-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多