算法:确定给定集合是否具有两个不相交的子集,以使两个子集中的元素之和相同?

时间:2018-08-14 19:50:01

标签: c algorithm data-structures

这是我编写的代码:-

#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},但不会显示出来。

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

2 个答案:

答案 0 :(得分:1)

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

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

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

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

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

#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

答案 1 :(得分:1)

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

这是一种更简单的方法:

  • 枚举所有子集
  • 对于每个子集,计算总和并将具有子集签名及其和的结构存储到数组中。
  • 将数组按和值排序
  • 对排序后的数组进行迭代:
  • 对于每个元素,以相同的总和遍历后续元素,如果其中一个元素具有不相交的子集签名,则您有一个匹配项,将其打印出来。
  • 空间复杂度为 O(2 N ,时间复杂度甚至更差为 O(2 N + 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 }