【问题标题】:C/C++ implementation of an algorithm similar to subset sum类似于子集和的算法的C/C++实现
【发布时间】:2010-01-28 07:56:31
【问题描述】:

这个问题比knapsack(或它的一种类型,没有值,只有正权重)更简单。问题在于检查一个数字是否可以是其他数字的组合。该函数应返回truefalse

例如,

112 和带有{ 17, 100, 101 } 的列表应该返回false469 具有相同的列表应该返回true35 应该返回false119 应该返回true,等等。 ..

编辑:子集和问题比背包更准确。

【问题讨论】:

  • 我很确定 119 应该返回 false..!
  • 天哪……现在还很早。
  • 7 不在列表中。如果你可以乘以不在列表中的数字,为什么不乘以 1.12 从 100 得到 112?
  • @Pete: 7 不在列表中,但 17 是,7 乘以 17 是 119,所以 119 应该返回 true !!!!!! grrr(一巴掌)

标签: c++ c algorithm implementation knapsack-problem


【解决方案1】:

这是子集和问题的一个特例,集合只包含一个负数(即将 112 和 { 17, 100, 101 } 表示为 { -112, 17, 100, 101 })。维基百科页面上有一些算法,http://en.wikipedia.org/wiki/Subset_sum_problem

【讨论】:

  • 这仍然没有考虑到集合中数字的倍数。尽管具有倍数的子集和是适用的,因为负数可以相乘。
【解决方案2】:

一个对您有帮助的观察是,如果您的列表是 {a, b, c...} 并且您要测试的数字是 x,那么只有当 x或者 x-a 可以写成子列表 {b, c, ...} 的总和。这让您可以编写一个非常简单的递归算法来解决问题。

编辑:这里有一些代码,考虑到下面的 cmets。没有经过测试,所以可能有问题;不一定是最快的。但是对于一个小数据集,它会很好地完成工作。

bool is_subset_sum(int x, std::list::const_iterator start, std::list::const_iterator end)
{
  // for a 1-element list {a} we just need to test a|x
  if (start == end) return (x % *start == 0); 

  // if x is small enough  we don't need to bother testing x - a
  if (x<a) return is_subset_sum (x, start+1, end);

  // the default case. Note that the shortcut properties of || means the process ends as soon as we get a positive.
  return (is_subset_sum (x, start+1, end) || is_subset_sum (x-a, start, end));
}

【讨论】:

  • 好吧,如果它必须对 x 求和并且没有负整数,也许我可以使用带有子集的常见(零和)实现:{ -x, a, b, c 。 .. }
  • 啊,好的。我将这个问题解释为简单地取列表子集的总和。如果您允许正倍数,那么您需要测试 x、x-a、x-2a 等。如果您允许负倍数,那么测试当然归结为 gcd(a,b,c...) 是否进入 x。
  • 递归步骤会稍微复杂一些:a 是解决方案的一部分,然后 x-a 可以根据 { a, b, c } 计算,或者 a 不是解决方案的一部分解决方案和x 可以写成{ b, c }。在任何情况下,您都在减少问题的大小(较小的数字或较小的集合)。现在搜索空间可能比你想用天真的实现做的更大......
【解决方案3】:

请注意,随着查询的数量变大,阳性结果会变得越来越密集。例如,所有大于 100^2 的数字都可以由 { 17, 100, 101 } 生成。所以最优算法可能取决于查询的数量是否远大于集合的成员。你可以研究场论。

至少,如果集合的最大公约数不在查询中,您至少知道结果总是错误的,并且可以在可忽略不计的时间内进行检查。

【讨论】:

    【解决方案4】:

    如果要到达的数字不是太大,您可能可以从集合中生成所有在 [1,N] 范围内的可到达数字。

    问题:使用列表L 中的元素到达N,其中N 足够小,不必担心大小为N 元素大小的向量。

    算法:

    • 生成大小为N 的向量V
    • 对于列表L 中的每个元素l
      • 对于v 中的每个可达元素V
        • 将 V 中的所有元素 v + n*l 标记为可访问

    【讨论】:

    • 我真的很喜欢这种方法,因为列表没有变化,而且最大数量也不是很大。
    • 又称筛子。这是我想到的第一件事,只是在 CS 中“这个算法适用于小数”是不受欢迎的。不要忘记使用bitset 将上限提高一个数量级。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 2017-07-04
    • 1970-01-01
    • 1970-01-01
    • 2019-01-02
    相关资源
    最近更新 更多