子集和的变量

时间:2016-09-18 05:58:41

标签: algorithm set subset-sum

鉴于3正整数n, k, and sum,找到k个不同的元素a_i,其中
a_i \in S, 1 <= i <= k, and a_i \neq a_j for i \neq j
并且,S是集合 S = {1, 2, 3, ..., n}
这样的 \sum_{i=1}^{k}{a_i} = sum
由于指数复杂性,我不想应用蛮力(检查所有可能的组合)来解决问题。有人能给我一个暗示解决这个问题的另一种方法吗?另外,我们如何利用集S排序的事实? 在这个问题上是否有O(k)的复杂性?

4 个答案:

答案 0 :(得分:1)

如何利用1..n设置属性的想法:

a开始的自然行的k个连续成员的总和是

sum = k*(2*a + (k-1))/2

要获得有关所需s的此类子序列的总和,我们可以解决

a >= s/k - k/2 + 1/2
or 
a <= s/k - k/2 + 1/2

比较ssum值并进行更正。

例如,有s=173n=40k=5,我们可以找到

a <= 173/5 - 5/2 + 1/2 = 32.6

对于第32位开始,我们有序列32,33,34,35,36sum = 170,对于3的校正,我们可以将39更改为39,或34,35,36更改为35,36,37,依此类推

似乎使用这种方法我们得到了O(1)的复杂性(当然,可能存在一些我错过的微妙之处)

答案 1 :(得分:0)

可以修改pseudo-polynomial algorithm for subset sum

准备尺寸为 k X sum 的矩阵 P ,并将所有元素初始化为0. P [p,q]的含义== 1 < / em>是 p 数字的一个子集汇总到 q P [p,q] == 0 表示这样尚未找到一个子集。

现在迭代 i = 1,...,n 。在每次迭代中:

  1. 如果i≤sum,设置 P [1,i] = 1 (有一个大小为1的子集实现 i )。

  2. 对于任何条目 P [p,q] == 1 ,您现在知道 P [p + 1,q + i] 现在应该是1也是。如果(p + 1,q + i)在矩阵的边界内,则设置 P [p + 1,q + i] = 1

  3. 最后,检查 P [k,sum] == 1

    假设所有整数数学运算都是常数,复杂性是Θ(n 2 sum)

答案 2 :(得分:0)

有一个O(1)(可以这么说)解决方案。接下来是@MBo对这个想法的正式发展(我希望)。

假设S是一组所有整数并找到最小解,这就足够了。解决方案K小于K' iff max(K) < max(K')。如果max(K) <= n,则K也是原始问题的解决方案;否则,原来的问题没有解决方案。

所以我们忽略n并找到K,这是一个最小的解决方案。设g = max(K) = ceil(sum/k + (k - 1)/2)s = g + (g-1) + (g-2) + ... (g-k+1)以及s' = (g-1) + (g-2) + ... + (g-k)。也就是说,s' s向下移动1.注意s' = s - k

显然s >= sum和(因为K很小)s' < sum

如果s == sum解决方案为K,我们就完成了。否则考虑集合K+ = {g, g-1, ..., g-k}。我们知道\sum(K+ \setminus {g}) < sum\sum(K+ \setminus {g-k}) > sum因此,K +的单个元素g_i就是\sum (K+ \setminus {g_i}) = sum。解决方案是K+ \setminus {\sum(K+)-sum}

4个整数a, b, c, d形式的解决方案,其中实际集合被理解为[a..b] \setunion [c..d],可以在O(1)中计算。

答案 3 :(得分:0)

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

unsigned long int arithmeticSum(unsigned long int a, unsigned long int k, unsigned long int n, unsigned long int *A);
void printSubset(unsigned long int k, unsigned long int *A);

int main(void)
{
    unsigned long int n, k, sum;
    // scan the respective values of sum, n, and k
    scanf("%lu %lu %lu", &sum, &n, &k);
    // find the starting element using the formula for the sum of an A.P. having 'k' terms
    // starting at 'a', common difference 'd' ( = 1 in this problem), having 'sum' = sum
    // sum = [k/2][2*a + (k-1)*d]
    unsigned long startElement = (long double)sum/k - (long double)k/2 + (long double)1/2;
    // exit if the arithmetic progression formed at the startElement is not within the required bounds
    if(startElement < 1 || startElement + k - 1 > n)
    {
        printf("-1\n");
        return 0;
    }
    // we now work on the k-element set [startElement, startElement + k - 1]
    // create an array to store the k elements
    unsigned long int *A = malloc(k * sizeof(unsigned long int));
    // calculate the sum of k elements in the arithmetic progression [a, a + 1, a + 2, ..., a + (k - 1)]
    unsigned long int currentSum = arithmeticSum(startElement, k, n, A);
    // if the currentSum is equal to the required sum, then print the array A, and we are done
    if(currentSum == sum)
    {
        printSubset(k, A);
    }
    // we enter into this block only if currentSum < sum
    // i.e. we need to add 'something' to the currentSum in order to make it equal to sum
    // i.e. we need to remove an element from the k-element set [startElement, startElement + k - 1]
    // and replace it with an element of higher magnitude
    // i.e. we need to replace an element in the set [startElement, startElement + k - 1] and replace
    // it with an element in the range [startElement + k, n]
    else
    {
        long int j;
        bool done;
        // calculate the amount which we need to add to the currentSum
        unsigned long int difference = sum - currentSum;
        // starting from A[k-1] upto A[0] do the following...
        for(j = k - 1, done = false; j >= 0; j--)
        {
            // check if adding the "difference" to A[j] results in a number in the range [startElement + k, n]
            // if it does then replace A[j] with that element, and we are done
            if(A[j] + difference <= n && A[j] + difference > A[k-1])
            {
                A[j] += difference;
                printSubset(k, A);
                done = true;
                break;
            }
        }
        // if no such A[j] is found then, exit with fail
        if(done == false)
        {
            printf("-1\n");
        }
    }
    return 0;
}

unsigned long int arithmeticSum(unsigned long int a, unsigned long int k, unsigned long int n, unsigned long int *A)
{
    unsigned long int currentSum;
    long int j;
    // calculate the sum of the arithmetic progression and store the each member in the array A
    for(j = 0, currentSum = 0; j < k; j++)
    {
        A[j] = a + j;
        currentSum +=  A[j];
    }
    return currentSum;
}

void printSubset(unsigned long int k, unsigned long int *A)
{
    long int j;
    for(j = 0; j < k; j++)
    {
        printf("%lu ", A[j]);
    }
    printf("\n");
}