在准备面试时我发现了以下问题:
3可写为1 + 1 + 1,1 + 2,2 + 1; 4可写为1 + 1 + 1 + 1,1 + 1 + 2,2 + 2,1 + 2 + 1,2 + 1 + 1,3 + 1,1 + 3;给定一个整数,有多少可能的表达式? (1 + 2和2 + 1不同)
因此,编写一个强力算法来计算它并获得设置它的所有数字集是非常简单的:
private static Set<List<Integer>> getIntegersSum(int num) {
Set<List<Integer>> returnSet = new HashSet<List<Integer>>();
if (num == 0) {
returnSet.add(new LinkedList<Integer>());
return returnSet;
}
for (int i = 1; i <= num; i++) {
Set<List<Integer>> listNMinI = getIntegersSum(num - i);
for (List<Integer> l : listNMinI) {
l.add(0, i);
returnSet.add(l);
}
}
return returnSet;
}
现在我相信描述此算法复杂性的递归关系是:
T(0) = \Theta (1)
T(n) = O(n) + \Sum_{i=0}^{n-1} T(i)
我不太确定如何从这种递归关系中获得很大的复杂性。我还想知道这个问题是否有一个封闭的形式解决方案(我们将为每个数字提供多少组合)。
如果我们通过缓存每个调用的结果来记忆这个算法(类似于你如何加速斐波纳契),我也不确定复杂性是多少。
答案 0 :(得分:2)
有2个 n - 1 - 1个这样的表达式。
有很多方法可以解决这个问题。
我使用了这个问题的结果:
有n个糖果。有多少种方法可以将所有n个糖果分成k个人(可以给0个糖果)?
顺序很重要,因为这些部分是针对不同的人。解是(n + k-1)C(k-1)。我们在混合中添加k-1个分隔符(这使得总和n + k-1),并且我们试图找到插入分隔符以将糖果分成k个部分的方式的数量。想想一行中的n + k - 1个盒子放置糖果和分隔符,我们想找到多种方法来为分隔符选择k - 1个槽,它将盒子分成k个部分。
回到这个问题,我们需要回答这个子问题:
将n表示为k个正数之和的方法有多少?
我们可以重用上面的糖果分裂问题的结果,但我们需要保留k以防止项为0.所以结果将是((n - k)+ k - 1)C(k - 1 ),简化为(n - 1)C(k - 1)。 ((n - k)是由于我们为k个项中的每个项都放了k。)
因此,最终结果将是Sum [i = 2..n](n-1)C(i-1),因为该表达式包含至少2个项,并且最多包含n个项。我们知道Sum [i = 1..n](n - 1)C(i - 1)= 2 n - 1 ,所以Sum [i = 2..n](n - 1 )C(i-1)= 2 n - 1 -1。
@MarkDickinson在评论中解释了解决此问题的另一种方法。推理更直接。
在每对糖果之间,要么是分离器,要么没有分离器。这立即给出
2^(n-1)
种可能性。无论出于何种原因,OP都排除了我们只有1个部分的单个案例,因此减去1得到2^(n-1) - 1
。
使争论更加稳固。由于问题只允许积极的术语,我们只能在糖果之间插入1个分隔符,我们只能将它们插入糖果之间,而不是2个末端。因此,分隔符可以出现(n-1)个位置。