我有一个相对复杂的问题,我需要一个算法来从一个总和为X的数组中找到所有可能的子数组,所以对于给定的数组:
{2,8,12,45,32,7,6,5}
假设我们需要总和为20的子数组,其中一些是:
{8,12} {2,7,6,5} {12,6,2}
然而会有如下组合:
{7,7,6} {5,5,5,5} {8,8,2,2}
我需要所有可能的金额。
我已经做了一个解决方案,对所有可能性进行强力检查,但是需要花费太长时间(在某些情况下超过30分钟)才能完成,所以我需要一个更智能的解决方案,我一直在讨论几天了。
答案 0 :(得分:1)
您的问题似乎表明重复数字的答案是可以接受的,并且您不希望生成可以订购数字的所有可能方式。我的答案就是基于此。
我在C ++中实现这一点。作为数据结构,我可能会使用这样的东西:
struct partial_sum {
int min_last_summand;
std::vector< std::pair<partial_sum*, int> > prefixes;
};
std::map<int, partial_sum*> m;
这里的核心部分是地图m
。它将总和的值映射到有关如何获取它的一些信息。您已将0
映射到NULL
进行初始化。 prefixes
成员将存储有关获取给定总和的所有可能方式的数据。每对的第一部分给出一个指向除最后一个之外的所有加数的信息的指针,而第二部分给出最后一个成员。这给出了一种有向无环图的形式,因为和可以是许多和的前缀,并且总和可以有许多不同的前缀,但每个前缀和的值小于当前总和的值。
中心迭代步骤将从m
移除最小元素,并生成所有可能的方法,您可以将输入集中的元素添加到刚删除的值。因此,您需要检查地图是否需要为新总和插入新条目。对于现有条目和新条目,您可以在prefixes
列表中创建一个新项目,其中您刚从地图中删除的指针作为第一部分,而您添加的最后一个加数作为第二部分。
我只以递增(或非降序)的顺序生成总和,以避免生成所有排列。为了简化操作,我会保留此min_last_summand
信息。它应始终包含prefixes
列表中对中所有第二个元素的最小值。生成新的和时,您可以跳过最后一个加数小于前缀的最小最后一个加数的那些,因为这意味着一个加数小于其前一个。您还可以避免在总值大于目标总和的情况下生成总和。
打印结果时,您必须递归可从目标总和到达的DAG部分,并列出从那里到根NULL
的所有路径。因此,在每个递归步骤中,您都有一个指向当前部分和的指针。如果该指针为NULL
,则发出由零加数组成的和。否则,您将遍历所有prefixes
。对于每个前缀,您可以递归以生成写入该前缀的所有可能方法,但前提是第一个元素的min_last_summand
不大于当前最后一个summand,并且仅当第二个元素不大于将跟随它的加数。这意味着您必须在summand之后将其作为递归调用的参数传递。总而言之,这可以避免产生具有降序的总和。
上述方法假设您的程序将在一次运行后终止,因此您不必担心释放内存。如果你这样做,你可能必须存储指向你创建的所有对象的指针,这样你就可以全部释放它们。