子集和 - 2D动态规划

时间:2011-11-12 19:25:23

标签: c algorithm

我正在尝试找到 ALL 一组n个数字的子集,其总和为m。例如,如果我有集(10,20,30,40,40,50,80)m = 90,则子集为(10,80)(20,30,40)(40,50)。我有一些代码,可以使用像......这样的小案例来查找。

M[i, j] = max(M[i − 1, j], M[i − 1, j − A[i])

然后使用...回溯。

if (M[n][m]!=0)
{
    for ( i = n; i >= 1; i --)
        if (M[i - 1][j]!=M[i][j])
        {
            printf ("Use item %d = %d\n", i, A[i]);
            j -= A[i];
        }
}

但是当我尝试更大的测试用例时,它不会返回所有子集。任何帮助都将非常感激。

2 个答案:

答案 0 :(得分:1)

所有正数都允许修剪,这大大降低了复杂性(虽然我不知道平均多少)。对于大型集合,您可能需要更精确的算法,但这里有:

  1. 将(多)集合排序为数组,比如nums;例如,nums = {10,20,30,40,40,50,80}
  2. 创建累积总和数组cum;这将是cum = {10,30,60,100,140,​​190,270}
  3. 现在(C-ish伪代码)

    void parts(target, index, set){  // assuming we only want to print the sets out
        if (target == 0){
            print out set;
            return;
        }
        while(index >= 0 && nums[index] > target) --index; // skip too large numbers
        if (index < 0 || cum[index] < target) return;  // done, all numbers too large or total too small
        for(; index >= 0 && cum[index] >= target; --index){  // we can stop when the total sum is too small
            add nums[index] to set;
            parts(target - nums[index], index-1, set);
            remove nums[index] from set;
            // if no duplicate sets are desired, skip duplicates, for the example,
            // (40,50) would be created twice without skipping:
            // while(index > 0 && nums[index-1] == nums[index]) --index;
        }
    }
    

答案 1 :(得分:0)

/* first we sort M with a quicksort, for instance, 
   whose an efficient implementation can be easily found on the web. */
M = quicksort(M);

/* we don't consider elements greater than m 
   so we compute the index of the greatest element to consider */
int max = n - 1;
for (; max > 0 && M[max] > m; max--);

int total = 1 << (max + 1);
for (int i = 0; i < total; i++)
{
    /* In this loop we'll go through all the possible subsets of M */
    int sum = 0;
    for (int k=0; k<n; k++)
    {
        if ((1 << k) & i)
        {
             s += M[k]; 

             /* if s is greater than m no need to go further */
             if (s > m)
             {
                 break;
             }
        }
    }
    if (sum == m)
    {
        /* Here we have found a successful subset so 
           we can output it however we want.
           It is composed of all the elements used 
           to build s in the loop above */        
    }
}