【问题标题】:How to find subsets that contains equal sum in an array:如何在数组中找到包含相等总和的子集:
【发布时间】:2018-09-21 19:53:53
【问题描述】:

如何在数组中找到包含相等总和的子集。 例如

{1,2,3,4,2}->{1,2,3} && {4,2}
{1,1,3,3,2,8}->{1,3,3,2}&&{1,8}
{1,3,4,7}->no subset

我尝试使用以下代码,但没有得到适当的输出。

import java.util.*

public static void main(String[] args) {
    int arr[] = {1,3,4,7};
    int sum = getSum(arr, arr.length);
    int[] solution = new int[arr.length];
    find(arr, 0, 0, sum, solution);
}    
public static int getSum(int arr[], int n) {
    float sum = 0f;
    for (int i = 0; i < n; i++)
        sum = sum + arr[i];
    return Math.round(sum / 2);
}
public static void find(int[] A, int currSum, int index, int sum,
        int[] solution) {
    if (currSum == sum) {
        System.out.println("\nSum found");
        for (int i = 0; i < solution.length; i++) {
            if (solution[i] == 1) {
                System.out.print("  " + A[i]);
            }
        }

    } else if (index == A.length) {
        return;
    } else {
        solution[index] = 1;// select the element
        currSum += A[index];
        find(A, currSum, index + 1, sum, solution);
        currSum -= A[index];
        solution[index] = 0;// do not select the element
        find(A, currSum, index + 1, sum, solution);
    }
    return;
}

使用此输入数组:1,2,3,4,2 低于输出

1  2  3
1  3  2
2  4
4  2

输入数组:1,1,3,3,2,8

1  3  3  2
1  8
1  3  3  2
1  8

输入数组:1,3,4,7

1  3  4
1  7

【问题讨论】:

  • 您的代码有编译错误。 getSum 方法无效,但您设置为 int。请修复。
  • 这是一个经典的问题,虽然不是那么容易:en.wikipedia.org/wiki/Subset_sum_problem
  • 我已经更新了 getSum 方法。
  • 您的代码正在计算所有可能的结果。那有什么问题呢?
  • 在对编程技术有相当了解之前不要学习语言和库。找到每个可能组合的总和并将其(连同组合)存储在排序映射(RB-tree 或其他)中。然后找到相等的和。

标签: java data-structures


【解决方案1】:

要解决这个问题,您的代码中至少需要有 3 个嵌套循环 sudocode 就像

for (i=0,i< array.length-1,i++){
 for(k=i;k<array.length-1,k++){ 
  fsum=findSum(i/*from*/,k/*to*/,array);
  ssum=0;

  for(x=k,x<array.length,x++){
    secondsum=findSum(k,x,array);
    if(fsum==ssum){
     prend(i,k,array);
     print(k,x,array);
     break;
    }

   }




 }



}

注意,这只是需要实现的sudo代码

【讨论】:

    【解决方案2】:

    代码的重复部分工作正常。在相当外围的部分只有两个小缺陷:

    1. 这两个子集仅在数字之和为偶数时才存在。您的代码未考虑此要求,这是导致 {1,3,4,7} 失败的原因。为此,主函数的主体稍作修改,以便仅对偶数和执行递归。

    2. 您应该整理出重复项,以防止与 {1,2,3,4,2} 和 {1,1,3,3,2,8} 相关的重复。为此,必须非常简单地存储结果子集。然后,在消除重复后,打印出剩余的子集。为方便起见,这两个功能都封装在添加的 SubsetPair 类中,该类提供了方法 storeSubsetPair 和 printSubsetPairList 分别用于存储和打印。

    修改后的代码为:

    import java.util.*;
    
    public class Main {
    
        public static void main(String[] args) {
            int arr[] = {1,3,4,7};
            int sum = getSum(arr, arr.length);
            int[] solution = new int[arr.length];
            // find(arr, 0, 0, sum, solution); // REPLACED WITH: -----------------
            if (sum % 2 == 0) { 
                sum = Math.round(sum / 2f);
                find(arr, 0, 0,sum, solution);
            }
            SubsetPair.printSubsetPairList(); 
            // -------------------------------------------------------------------
        }    
        public static int getSum(int arr[], int n) {
            // float sum = 0f; // REPLACED WITH: ---------------------------------
            int sum = 0;
            // -------------------------------------------------------------------
            for (int i = 0; i < n; i++)
                sum = sum + arr[i];
            // return Math.round(sum / 2); // REPLACED WITH: ---------------------
            return sum;
            // -------------------------------------------------------------------
        }
        public static void find(int[] A, int currSum, int index, int sum,
                int[] solution) {
            if (currSum == sum) {
                // System.out.println("\nSum found"); // REPLACED WITH: ----------
                // for (int i = 0; i < solution.length; i++) {
                //    if (solution[i] == 1) {
                //        System.out.print("  " + A[i]);
                //    }
                // }
                SubsetPair.storeSubsetPair(A, solution);
                // ---------------------------------------------------------------
            } else if (index == A.length) {
                return;
            } else {
                solution[index] = 1;// select the element
                currSum += A[index];
                find(A, currSum, index + 1, sum, solution);
                currSum -= A[index];
                solution[index] = 0;// do not select the element
                find(A, currSum, index + 1, sum, solution);
            }
            return;
        }
    }
    
    //NEW: Class for storage and print:
    class SubsetPair {
    
        private static List<SubsetPair> subsetPairList = new ArrayList<>();
    
        private List<Integer> subset1 = new ArrayList<>();
        private List<Integer> subset2 = new ArrayList<>();
    
        //
        // Storage of the subset pair
        //
        public static void storeSubsetPair(int[] A,  int[] solution) {
            SubsetPair subsetPair = new SubsetPair();
            for (int i = 0; i < solution.length; i++) {
                if (solution[i] == 1) {
                    subsetPair.subset1.add(A[i]);
                } else {
                    subsetPair.subset2.add(A[i]); 
                }
            } 
            if (!subsetPair.isDuplicate()) {
                subsetPairList.add(subsetPair);
            }
        }   
    
        // Remove duplicates
        private boolean isDuplicate() {
            for (SubsetPair subsetPair : subsetPairList) {
                if (isEqual(subset1, subsetPair.subset2) && isEqual(subset2, subsetPair.subset1) || 
                    isEqual(subset1, subsetPair.subset1) && isEqual(subset2, subsetPair.subset2)) {
                    return true;
                }
            }
            return false;
        }
    
        private boolean isEqual(List<Integer> subset1, List<Integer> subset2) {
            return subset1.containsAll(subset2) && subset2.containsAll(subset1);
        }
    
        //
        // Output of the subset pairs
        //
        public static void printSubsetPairList() {
            if (subsetPairList.size() == 0) {
                System.out.println("No subset-pairs found!");
            } else {
                for (int i = 0; i < subsetPairList.size(); i++) {
                    subsetPairList.get(i).printSubsetPair(i + 1);
                }
            }
        }
    
        private void printSubsetPair(int i) {
            System.out.print(i + ". Subset-Pair:\n");
            System.out.print("Subset 1: ");
            for (Integer i1 : subset1) {
                System.out.print(i1 + " ");
            }
            System.out.println();
            System.out.print("Subset 2: ");
            for (Integer i2 : subset2) {
                System.out.print(i2 + " ");
            }
            System.out.print("\n\n");
        }
    }
    

    通过这些更改,输出变为:

    对于偶数和为 12 的 {1,2,3,4,2}:

      1. Subset-Pair:
      Subset 1: 1 2 3 
      Subset 2: 4 2 
    

    对于偶数和为 18 的 {1,1,3,3,2,8}:

      1. Subset-Pair:
      Subset 1: 1 3 3 2 
      Subset 2: 1 8 
    

    对于奇数和为 15 的 {1,3,4,7}:

      No subset-pairs found!
    

    对于偶数和为 10 的 {3,1,1,2,2,1}:

      1. Subset-Pair:
      Subset 1: 3 1 1 
      Subset 2: 2 2 1 
    
      2. Subset-Pair:
      Subset 1: 3 2 
      Subset 2: 1 1 2 1 
    

    对于偶数和为 14 的 {2,4,8}:

      No subset-pairs found!
    

    正如在 cmets 中已经说明的那样,该问题与“分区问题”有关,该问题是决定是否可以将多个正整数集划分为两个总和相等的子集的任务。对此进行了详细解释,例如在 https://en.wikipedia.org/wiki/Partition_problemhttps://www.geeksforgeeks.org/partition-problem-dp-18/ (后者包括 Java 实现)。但是这两个问题并不完全相同,因为“分区问题”的解决方案只回答了如果多重集可以被分成两个子集的问题,但不能明确确定子集本身。

    【讨论】:

      猜你喜欢
      • 2019-04-04
      • 2013-10-30
      • 1970-01-01
      • 2019-11-07
      • 2011-09-05
      • 2022-10-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多