【发布时间】:2015-06-17 13:37:24
【问题描述】:
对于A = {1, 2, 3, ..., n} 形式的一组。它被称为集合A 的划分,一组k<=n 元素遵循以下定理:
a) A 的所有分区的并集是A
b) A 的两个分区的交集是空集(它们不能共享相同的元素)。
例如。 A = {1, 2,... n}我们有分区:
{1, 2, 3}
{1, 2} {3}
{1, 3} {2}
{2, 3} {1}
{1} {2} {3}
这些理论概念在我的算法教科书中都有介绍(顺便说一下,这一小节是“回溯”一章的一部分)。我应该找到一种算法来生成给定集合的所有分区。我整天都在努力解决这个问题,但我找不到解决方案。你能解释一下这个算法是如何工作的吗?另外,能给我一个算法的伪代码草图吗?
【问题讨论】:
-
提示:假设您拥有 {1, ..., n-1} 的所有分区的所有集合。 (顺便说一句,分区中的各个集合通常称为“部分”。)有没有办法通过以某种方式将元素 n 注入每个分区来生成 {1, ..., n} 的每个分区?这是否会生成 {1, ..., n} 的每个分区一次且仅一次?
-
是的,到目前为止你是对的。有两件事要记住: 1. 当一个分区包含多个部分时(例如您的示例中的
{1} {2}),您需要尝试将新元素添加到每个部分——因此您将创建一个全新的 每个部分的分区。 2.您还需要创建分区,其中新号码是自己的一部分。 -
我在编辑之前回复了您的评论。您只需要考虑如何从前一个分区生成当前分区;要生成前一个,您只需调用自己(递归)。 (你还需要处理一个基本情况——当集合为空时。)如果你展开执行,这将在 4 之前插入 3。
-
@cristid9:您对将分区重写为数组的观察很重要;它导致了一个简单的迭代解决方案,我将其作为答案提交,以及此类枚举的一般策略。
-
@cristid9:正如我在回答中所说,使这个 O(1) 需要保持最大值而不是重新计算它们。在您的递归解决方案中,您可以通过将最大值作为第五个参数传递来做到这一点。在对
printPartitions的递归调用中,您传递了i > maximum ? i : maximum。通过观察在除最后一个之外的所有递归调用中,您只需传递最大值,而在最后一个调用中传递最大值+1,这可以提高效率。这稍微简化了代码。见ideone.com/6vaXaN
标签: algorithm set combinatorics backtracking