使用递归的子集求和

时间:2015-01-05 16:56:45

标签: c++ algorithm binary-tree subset backtracking

我的输入是:

W[10] = {1, 3, 5, 7, 9, 12, 19, 22, 36, 63}
X[10] = {0};
M = 79;

我通过以下方式调用了该函数:

findSolution(0,0,177); <br>

注意:177是W数组中所有元素的总和。

void findSolution(int s, int k, int r) {
    cout << "fn(" << s << " , " << k << ", " << r << " )" << endl;
    X[k] = 1;
    if (s + W[k] == M){
        printArr(X);
    }
    else if (s + W[k] + W[k + 1] <= M) {
        return findSolution(s + W[k], k + 1, r - W[k]);
    }

    if ((s + r - W[k] >= M) && (s + W[k + 1]) <= M){
        X[k] = 0;
        return findSolution(s, k + 1, r - W[k]);
    }
}

输出:

fn(0 , 0, 177 )
fn(1 , 1, 176 )
fn(4 , 2, 173 )
fn(9 , 3, 168 )
fn(16 , 4, 161 )
fn(25 , 5, 152 )
fn(37 , 6, 140 )
fn(56 , 7, 121 )

上面给出的输出是跟踪函数调用。输出在此结束,不会继续。我的代码有什么问题。我正在尝试打印一个子集,它给出了所需的sum = 79.递归调用不会返回。

2 个答案:

答案 0 :(得分:0)

递归调用 返回;它只是在您找到解决方案之前这样做。如果达到最后if但失败,则会发生这种情况。 (请注意,当您调用printArr时,看起来像您的基本情况,必然会停止递归。)

答案 1 :(得分:0)

您的解决方案的问题在于它使用了greedy策略(即,在找到合适的候选人后,它没有&#34;回溯&#34;)

您的算法会检查三个条件:

  • 您找到了解决方案,
  • 如果 k - 元素添加到子集中,或者
  • ,则可以使用解决方案
  • 如果替换 k-1 - st元素与k - th。
  • ,则可以使用解决方案。

此策略并未用尽所有可能性:例如,可能无法用k - st替换k+1 - 元素,但可能会在k之前替换多个元素{1}} - 与k+1 - st并获得解决方案。你的策略是贪婪的,因为当它发现一个元素可以添加到一个集合(即s + W[k] + W[k + 1] <= M)时,它会采用该路径,并且永远不会回头(即从该分支返回)。

您可以通过重构代码来解决此问题:

  • 找到解决方案时让您的函数返回true,否则返回false
  • 保留基本案例if (s + W[k] == M),并在找到解决方案时添加return true
  • 检查是否可以将k - 元素添加到集合中。如果可能,请添加它,并尝试s + W[k]
  • 的部分总和
  • 检查递归调用的返回。如果是true,请返回true
  • 否则,从集合中删除k - 元素,并在混合中没有k - 元素的情况下进行第二次递归调用。使用s
  • 的相同部分和
  • 将最后一次递归调用的值返回给调用者。

现在你的算法是穷举的,因为对于每个元素,当元素是解的一部分时,算法试图找到一个部分和,当元素不是解的一部分时(即O(2 n) )检查所有)。