c++ - 分区问题的递归函数

标签 c++ recursion

问题是找出数组中数字的子集加起来达到特定目标数字的次数。

例如,有两种方法可以对集合{1, 3, 4, 5}进行分区,使剩余元素加起来为5:

  • 选择14
  • 仅选择5

相比之下,无法对集合 {1, 3, 4, 5} 进行分区以获得 11。

#include "genlib.h"
#include <iostream>

void RecursePart(int[], int, int, int&);
int Wrapper(int[], int, int);


int main() {
    int arr[8] = {1,2,3,4,5,6,7,8};
    cout << Wrapper(arr, 8, 11);
}


void RecursePart(int arr[], int len, int target, int& ctr) {
    if (len == 1) {
        if (arr[0] == target) {
            ctr++;
        }
        return; 
    }

    int sum, temp;
    sum = temp = arr[0];

    for (int j = 1; j < len; j++) {
        if (sum == target) {
            ctr++;
            break;
        }

        sum = sum + arr[j];

        if (sum == target) {
            ctr++;
            sum = temp;
            continue;           
        }

        if (sum > target) {
            sum = temp;
            continue;
        }

        if (sum < target) {
            temp = sum;
            continue;
        }    
    }

    RecursePart(arr + 1, len - 1, target, ctr);
}

int Wrapper(int arr[], int len, int target) {
    int n = 0;
    RecursePart(arr, len, target, n);
    return n;
}

问题是我得到的输出是 1,但是数组中数字的子集加起来为 11 的次数大于 1。我尝试跟踪该算法,并且知道问题所在必须在for循环中。算法会跳过一些求和。我怎样才能解决这个问题?

最佳答案

正如其他人所说,这是子集和问题(NP 完全问题),这意味着您需要指数时间算法来解决它。
只需查看您的函数,您就可以调用 RecursePart 一次,每次都使用 len-1,然后有一个长度为 nfor-loop ,这意味着您的计算量为 O(n^2)。这显然无法解决 O(2^n) 问题。

以下是一个递归解决方案,它创建子集的总和,并尝试查看它们是否达到目标。如果当前子集没有等于目标的选项,则停止当前子集的“创建”。

int RecursePart(int arr[], int len, int idx, int curr_sum, int target)     
{        
    int count = 0;

    // this subset is good
    if (curr_sum == target)
       return 1;

    // the sum of the current subset exceeds target, no point in continuing
    if (curr_sum > target || idx == len)
       return 0;

    count += RecursePart(arr, len, idx+1, curr_sum + arr[idx], target);
    count += RecursePart(arr, len, idx+1, curr_sum, target);

    return count;
}

这是我之前的解决方案,它创建所有可能的子集,并计算与目标匹配的子集。

#include <iostream>

int Wrapper(int[], int, int);

int main() {
    int arr[8] = {1,2,3,4,5,6,7,8};
    std::cout << Wrapper(arr, 8, 11);
}

// counts the sum of a subset
int CountSet(int* arr, int* mask, int len)
{
    int sum = 0;

    for (int i=0; i < len; ++i)
    {
        if (mask[i])
        {
            sum += arr[i];
        }
    }
    return sum;
}


int RecursePart(int arr[], int idx, int len, int* subset_mask, int target) 
{
    int count = 0;

    if (idx == len)
    {
        if (CountSet(arr, subset_mask, len) == target)
            return 1;
        else
            return 0;
    }

    // create the subset "without" me
    subset_mask[idx] = 0;
    count += RecursePart(arr, idx+1, len, subset_mask, target);

    // now create the subset "with" me
    subset_mask[idx] = 1;
    count += RecursePart(arr, idx+1, len, subset_mask, target);

    return count;
}

int Wrapper(int arr[], int len, int target) {

    int* subset_mask = (int*)malloc(len*sizeof(int));
    int res = RecursePart(arr, 0, len, subset_mask, target);
    free(subset_mask);
    return res;
}

关于c++ - 分区问题的递归函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7374644/

相关文章:

haskell - Haskell 中的回溯数独

c - 递归寻找主要因素

c++ - 带有 ncurses 的子窗口

c++ - 当将值复制到现有对象中时,(N)RVO 是否也会发生?

c++ - C++ 中的单例习语

algorithm - 教授递归技术的最佳方法是什么?

c++ - 使用带有指针/类的 set 方法

C++:sqlite3 是否使用 errno 代码?

java - 使用递归检查周围的单元格

c++ - 为什么这个递归函数的行为与预期的不同?