【问题标题】:Algorithms: To determine whether a given set has two subsets which are disjoint such that sum of elements in both subsets is same?算法:确定给定集合是否有两个不相交的子集,使得两个子集中的元素总和相同?
【发布时间】:2018-08-14 19:50:01
【问题描述】:

这是我写的代码:-

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int ctr = 0;

void partition(int arr[], int n) {
    int i, j, k = 0, r, in = 0, te, flag = 0;
    int *sum;
    sum = (int*)malloc(sizeof(int) * pow(2, n));
    for (i = 0; i < pow(2, n); i++) {
        printf("{");
        for (j = 0; j < n; j++) {
            if (i & (1 << j)) {
                printf("%d ", arr[j]);
                sum[k] = sum[k] + arr[j];
            }
        }
        k++;
        printf("}");
        printf("\n");
    }
    printf("\n \n");

    int *temp;
    for (k = 0; k < pow(2, n) - 1; k++) {
        in = 0;
        temp = (int*)malloc(sizeof(int));
        for (r = k + 1; r < pow(2, n); r++) {
            if (sum[k] == sum[r]) {
                printf("\n Printing solution for sum %d", sum[k]);
                for (i = 0; i < pow(2, n); i++) {
                    if (i == k || i == r) {
                        printf("{");
                        for (j = 0; j < n; j++) {
                            if (i & 1 << j) {
                                for (te = 0; te < in; te++) { //Disjointness
                                    if (temp[te] == arr[j])
                                        flag = 1;
                                }
                                if (flag == 1)
                                    break;
                                temp[in++] = arr[j];
                                temp = (int*)realloc(temp, sizeof(int));
                                printf("%d ", arr[j]);
                            }
                        }
                        printf("}");
                        printf("\n");
                    }
                }
                break;          
            }
        }
        free(temp);
    }
}

void main() {
    int *arr, n, i;
    printf("\n Enter the number of elements in the set \n");
    scanf("%d", &n);
    arr = (int*)malloc(sizeof(int) * n);
    printf("\n Enter the set elements \n");
    for (i = 0; i < n; i++) {
        scanf("%d", &arr[i]);
    }
    partition(arr, n);
}

它非常适合输入 {1,2,3}。对于{1,2,3,4},它也给出了正确的输出。

对于{1,2,3,4},输出为:

Printing solution for sum 3{1 2 }
{3 }

 Printing solution for sum 4{1 3 }
{4 }

 Printing solution for sum 5{2 3 }
{1 4 }

 Printing solution for sum 6{1 2 3 }
{}

 Printing solution for sum 7{}
{}

但是如果输入是{1,5,11,5},那么输出是:

 Printing solution for sum 5{5 }
{}

 Printing solution for sum 6{}
{}

 Printing solution for sum 11{}
{}

 Printing solution for sum 16{}
{}

 Printing solution for sum 17{}
{}

由于{1,5,5}{11} 是一个解决方案,在这种情况下,预期的输出应该是Printing Solution for sum 11 {1,5,5} and {11},但它没有显示。

问题是什么,我该如何解决?

【问题讨论】:

  • 对于不相交的集合,{1,5,11,5} 是否是有效数据,因为数字 5 不止一次?
  • temp=(int*)realloc(temp,sizeof(int)); 仅分配给 1 int。我希望temp=realloc(temp,sizeof *temp * (in+1)); 或类似的东西。
  • 数据是有效的,因为问题表明子集应该是不相交的,在这种情况下 {1,5,5} 和 {11} 是不相交的。
  • 你的问题有一些问题需要解决:a)它声明“给定一个集合......”然后你的一个例子是 {1, 5, 11, 5} 这不是一个集合。 b)然后它说“可以分成两个不相交的集合......”这对我来说意味着你不能从给定的集合中遗漏某些项目。组合时不相交的集合应该返回原始集合。但是您的示例之一 {1, 2,3, 4} 和 sum = 4 ,解决方案是 {1,3}, {4} 而您遗漏了 2。所以要明确问题陈述。
  • 该程序应该将一个集合作为输入,我使用了蛮力算法来生成给定集合的幂集。现在,我必须找到两个子集,使得它们的和相等,并且这两个子集应该是不相交的。

标签: c algorithm data-structures


【解决方案1】:

你的算法太复杂了。您从正确的路径开始,计算集合中所有子集的总和,但您没有正确计算所有可能的不相交子集以尝试找到具有相同总和的子集。

这是一个更简单的方法:

  • 枚举所有子集
  • 对于每个子集,计算总和并将具有子集签名及其总和的结构存储到数组中。
  • 按总和的值对数组进行排序
  • 遍历排序后的数组:
  • 对于每个元素,迭代具有相同总和的后续元素,如果其中一个具有不相交的子集签名,则匹配,打印它。
  • 空间复杂度为O(2N),时间复杂度更差O(2N+1 ),这很糟糕,但对于小型套装来说是可以管理的。

这是修改后的版本:

#include <stdio.h>
#include <stdlib.h>

struct s { int sig, sum; };

int compare(const void *p1, const void *p2) {
    int sum1 = ((const struct s*)p1)->sum;
    int sum2 = ((const struct s*)p2)->sum;
    return (sum1 > sum2) - (sum1 < sum2);
}

void print_set(int arr[], size_t sig) {
    printf("{");
    while (sig) {
        if (sig & 1)
            printf(" %d", *arr);
        arr++;
        sig >>= 1;
    }
    printf(" } ");
}

void partition(int arr[], int n) {
    size_t i, j, count = 1ULL << n;
    struct s *array = calloc(sizeof(*array), count);
    int b, sum;

    if (array != NULL) {
        for (i = 1; i < count; i++) {
            sum = 0;
            for (b = 0; b < n; b++) {
                if (i & (1ULL << b))
                    sum += arr[b];
            }
            array[i].sig = i;
            array[i].sum = sum;
        }
        qsort(array, count, sizeof(*array), compare);
        for (i = 0; i < count; i++) {
            for (j = i + 1; j < count && array[i].sum == array[j].sum; j++) {
                if ((array[i].sig & array[j].sig) == 0) {
                    printf("solution with sum=%d: ", array[i].sum);
                    print_set(arr, array[i].sig);
                    print_set(arr, array[j].sig);
                    printf("\n");
                }
            }
        }
        free(array);
    }
}

int main() {
    int *arr, n, i;
    printf("Enter the number of elements in the set: ");
    if (scanf("%d", &n) == 1) {
        arr = (int*)malloc(sizeof(int) * n);
        if (arr != NULL) {
            printf("Enter the set elements: ");
            for (i = 0; i < n; i++) {
                if (scanf("%d", &arr[i]) != 1)
                    return 1;
            }
            partition(arr, n);
            free(arr);
        }
    }
    return 0;
}

输出:

Enter the number of elements in the set: 3
Enter the set elements:  1 2 3
solution with sum=3: { 3 } { 1 2 } 

Enter the number of elements in the set: 4
Enter the set elements: 1 2 3 4
solution with sum=3: { 1 2 } { 3 }
solution with sum=4: { 4 } { 1 3 }
solution with sum=5: { 2 3 } { 1 4 }

Enter the number of elements in the set: 4
Enter the set elements: 1 5 11 5
solution with sum=5: { 5 } { 5 }
solution with sum=11: { 11 } { 1 5 5 }

【讨论】:

    【解决方案2】:

    您可能会发现以下是一个起点。这个示例程序所做的是获取一组值,然后生成尽可能不相交的子集,其中每个子集的元素之和在两个子集之间相等。

    我不确定这是否真的回答了您的问题。规定的要求相当宽松。您提供的源代码无法使用 Visual Studio 进行编译,并且当一些基本编译错误得到纠正时,生成的程序会崩溃。我还发现您的示例输出令人困惑。

    此程序创建的不相交子集的标准是:

    • 一个子集中的值不能出现在另一个子集中
    • 成员资格通过分配给超集每个元素的索引进行排序和跟踪
    • 一个子集可能有重复的值,但没有重复的索引元素
    • 空集不被视为有效的不相交子集,因为它没有元素
    • 必须至少有两个可以从集合中形成的子集
    • 只考虑两个子集,可能还有更多(例如,{1, 2, 3, 4, 5} 可能有 {2,3}, {1,4}, {5})。

    源代码如下。这来自单个 C 源代码文件,该文件是 Visual Studio C++ 解决方案的一部分,其中 C++ main() 调用 C 源代码入口点 mymain2()。我这样做是因为它对我来说更容易。

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    typedef struct {
        int *pSet;        // array of values. array is malloced when max number is known.
        int  nSetMax;     // max number of array elements, amount malloced.
        int  nSetCur;     // current number of in use array elements.
    } SetType;
    
    static void generateSet (int i, int arr[], int n, SetType *temp)
    {
        int j;
    
        temp->nSetCur = 0;
        for (j = 0; j < n; j++)
        {
            if ( i & (1 << j))
            {
                int te;
                int flag = 0;
    
                for (te = 0; te < temp->nSetCur; te++)
                {
                    // we compute disjoint property by the order of the elements in
                    // the set using element index in order to allow duplicate values per
                    // request of the question. Exampe 1, 5, 11, 5 as list of values.
                    if (temp->pSet[te] == j)
                        flag = 1;
                }
                if (flag == 1)
                    continue;
                temp->pSet[temp->nSetCur] = j;
                temp->nSetCur++;
            }
        }
    }
    
    static void printItems (int arr[], const SetType temp)
    {
        if (temp.nSetCur > 0) {
            int j;
            int sum = 0;
    
            printf("    {");
            for (j = 0; j < temp.nSetCur; j++) {
                printf("%2d ", arr[temp.pSet[j]]);
                sum += arr[temp.pSet[j]];
            }
            printf("} = %3d\n", sum);
        } else
            printf ("    {}\n");
    }
    
    // determine if the two sets are disjoint by comparing the element indexes
    // of the elements in each subset. the element index is the position of the
    // subset element in the set from which the subset was drawn.
    static int checkSetsByIndex (const SetType tempI, const SetType tempR)
    {
        int i;
    
        for (i = 0; i < tempI.nSetCur; i++) {
            int j;
            for (j = 0; j < tempR.nSetCur; j++) {
                if (tempI.pSet[i] == tempR.pSet[j])
                    return 0;
            }
        }
    
        return 1;
    }
    
    // determine if the two sets are disjoint by comparing the element values
    // of the elements in each subset. the element value is the value of the
    // subset element in the set from which the subset was drawn.
    // we may have duplicate values but as long as they are not in two different
    // subsets then we allow it.
    static int checkSetsByValue (int arr[], const SetType tempI, const SetType tempR)
    {
        int i;
    
    #if 1
        // following code does a check that the two sets are disjoint by value
        // meaning they do not share any values. however a set may have multiple
        // elements with the same value.
        // if not needed then you can turn it off with Preprocessor check.
        for (i = 0; i < tempI.nSetCur; i++) {
            int j;
            for (j = 0; j < tempR.nSetCur; j++) {
                if (arr[tempI.pSet[i]] == arr[tempR.pSet[j]])
                    return 0;
            }
        }
    #endif
    
        return 1;
    }
    
    static void partition(int arr[], int n)
    {
        int i;
        int iPow2n = pow(2, n);
        int *sum = calloc(iPow2n, sizeof(int));
    
        printf ("Generate and list sums\n");
        for(i = 0; i < iPow2n; i++)
        {
            int j;
            printf("  {");
            for (j = 0; j < n; j++)
            {
                if (i & (1 << j))
                {
                    printf("%2d ", arr[j]);
                    sum[i] = sum[i] + arr[j];
                }
            }
            printf("} = %3d\n", sum[i]);
        }
    
        printf("\n\nGenerate list of disjoint sets for each sum.\n");
    
        for (i = 0; i < iPow2n - 1; i++)
        {
            int r;
            SetType tempI = {calloc(iPow2n, sizeof(SetType)), iPow2n, 0};
            SetType tempR = {calloc(iPow2n, sizeof(SetType)), iPow2n, 0};
    
            for(r = i + 1; r < iPow2n; r++)
            {
                if(sum[i] == sum[r])
                {
                    generateSet (i, arr, n, &tempI);
                    generateSet (r, arr, n, &tempR);
                    // check disjoint subsets by looking at the index of the
                    // subset elements where the index is the index of the original
                    // set from which the subset is drawn.
                    if (checkSetsByIndex (tempI, tempR)) {
                        // check that the two subsets are disjoint by element values
                        // as well. this means that while a subset may have duplicate
                        // values, the elements of each subset must be disjoint can not
                        // have elements whose values are the same.
                        // so if we have a set {1, 5, 11, 5} then subsets of
                        // {5}, {5} is invalid but {1, 5, 5}, {11} is valid.
                        if (checkSetsByValue (arr, tempI, tempR)) {
                            printf("\n Printing solution for sum %d\n", sum[i]);
                            printItems (arr, tempI);
                            printItems (arr, tempR);
                            break;
                        }
                    }
                }
            }
            free(tempI.pSet);
            free(tempR.pSet);
        }
    }
    
    int mymain2(void)
    {
        int n;
        printf("\n Enter the number of elements in the set \n");
        scanf("%d",&n);
    
        if (n > 0) {
            int i;
            int *arr = malloc(sizeof(int) * n);
            printf("\n Enter the set elements \n");
            for (i = 0; i < n; i++)
            {
                scanf("%d",&arr[i]);
            }
            partition(arr,n);
    
            free (arr);
        }
    
        return 0;
    }
    

    对于元素为 {1, 2, 3, 4} 的 4 个值的集合,它会生成以下输出:

     Enter the number of elements in the set
    4
    
     Enter the set elements
    1 2 3 4
    Generate and list sums
      {} =   0
      { 1 } =   1
      { 2 } =   2
      { 1  2 } =   3
      { 3 } =   3
      { 1  3 } =   4
      { 2  3 } =   5
      { 1  2  3 } =   6
      { 4 } =   4
      { 1  4 } =   5
      { 2  4 } =   6
      { 1  2  4 } =   7
      { 3  4 } =   7
      { 1  3  4 } =   8
      { 2  3  4 } =   9
      { 1  2  3  4 } =  10
    
    
    Generate list of disjoint sets for each sum.
    
     Printing solution for sum 3
        { 1  2 } =   3
        { 3 } =   3
    
     Printing solution for sum 4
        { 1  3 } =   4
        { 4 } =   4
    
     Printing solution for sum 5
        { 2  3 } =   5
        { 1  4 } =   5
    

    对于一组 4 个值 {1, 5, 11, 5},它会生成以下输出:

     Enter the number of elements in the set
    4
    
     Enter the set elements
    1 5 11 5
    Generate and list sums
      {} =   0
      { 1 } =   1
      { 5 } =   5
      { 1  5 } =   6
      {11 } =  11
      { 1 11 } =  12
      { 5 11 } =  16
      { 1  5 11 } =  17
      { 5 } =   5
      { 1  5 } =   6
      { 5  5 } =  10
      { 1  5  5 } =  11
      {11  5 } =  16
      { 1 11  5 } =  17
      { 5 11  5 } =  21
      { 1  5 11  5 } =  22
    
    
    Generate list of disjoint sets for each sum.
    
     Printing solution for sum 11
        {11 } =  11
        { 1  5  5 } =  11
    

    对于一组 5 个值 {1, 2, 3, 4, 5},它会生成以下输出:

     Enter the number of elements in the set
    5
    
     Enter the set elements
    1 2 3 4 5
    Generate and list sums
      {} =   0
      { 1 } =   1
      { 2 } =   2
      { 1  2 } =   3
      { 3 } =   3
      { 1  3 } =   4
      { 2  3 } =   5
      { 1  2  3 } =   6
      { 4 } =   4
      { 1  4 } =   5
      { 2  4 } =   6
      { 1  2  4 } =   7
      { 3  4 } =   7
      { 1  3  4 } =   8
      { 2  3  4 } =   9
      { 1  2  3  4 } =  10
      { 5 } =   5
      { 1  5 } =   6
      { 2  5 } =   7
      { 1  2  5 } =   8
      { 3  5 } =   8
      { 1  3  5 } =   9
      { 2  3  5 } =  10
      { 1  2  3  5 } =  11
      { 4  5 } =   9
      { 1  4  5 } =  10
      { 2  4  5 } =  11
      { 1  2  4  5 } =  12
      { 3  4  5 } =  12
      { 1  3  4  5 } =  13
      { 2  3  4  5 } =  14
      { 1  2  3  4  5 } =  15
    
    
    Generate list of disjoint sets for each sum.
    
     Printing solution for sum 3
        { 1  2 } =   3
        { 3 } =   3
    
     Printing solution for sum 4
        { 1  3 } =   4
        { 4 } =   4
    
     Printing solution for sum 5
        { 2  3 } =   5
        { 1  4 } =   5
    
     Printing solution for sum 5
        { 1  4 } =   5
        { 5 } =   5
    
     Printing solution for sum 6
        { 2  4 } =   6
        { 1  5 } =   6
    
     Printing solution for sum 7
        { 3  4 } =   7
        { 2  5 } =   7
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-04
      • 1970-01-01
      • 2011-07-25
      相关资源
      最近更新 更多