Java 对集合中的项目求和以获得目标值的方法的数量-顺序很重要
我正在练习编程面试,我在GlassDoor上偶然发现了一个问题:“找出我们可以对集合中的项目求和的方法,以获得我们的目标值。顺序很重要。” 显然,面试官无法给出答案,但他在GlassDoor上留下了这样的评论:“这更多的是一个数学问题,而不是一个编程问题。” 此问题似乎与此问题不同: 所以我的问题是:考虑到顺序的重要性,解决这个问题的正确方法是什么?还有,一个有效的算法是什么,可以解决所有方法求和集合中的项目以达到目标值的问题,以及排序问题 如果你能提供工作代码,那就太棒了。另外,我在用Java练习,所以我更喜欢Java解决方案,但任何语言都可以 非常感谢。使用动态规划。假设我们有项目Java 对集合中的项目求和以获得目标值的方法的数量-顺序很重要,java,algorithm,search,combinations,permutation,Java,Algorithm,Search,Combinations,Permutation,我正在练习编程面试,我在GlassDoor上偶然发现了一个问题:“找出我们可以对集合中的项目求和的方法,以获得我们的目标值。顺序很重要。” 显然,面试官无法给出答案,但他在GlassDoor上留下了这样的评论:“这更多的是一个数学问题,而不是一个编程问题。” 此问题似乎与此问题不同: 所以我的问题是:考虑到顺序的重要性,解决这个问题的正确方法是什么?还有,一个有效的算法是什么,可以解决所有方法求和集合中的项目以达到目标值的问题,以及排序问题 如果你能提供工作代码,那就太棒了。另外,我在用Java
[1,2,3,4,5]
,目标是10
。为了解决这个问题,我们将使用子集[1]
,[1,2]
计算从0到10的任何值的目标方法数<代码>[1,2,3,4,5]
那么,如果我们已经知道了用[1,2,3]
对0到10之间的任何值求和的方法的数量,该怎么办?如何获得将任意值与另一项求和的方法数:[1,2,3,4]
?这很简单:我们不能简单地包含这个新项目:它是所有以前的值。对于每个目标值x,前面的集合“[1,2,3]”中增加了一个路径数=路径数目标值(x-4):让我们用表来说明:
| sum | 1 | 2 | 3 | 4 |
| 1 | 1 | 1 |(1)| 1 |
| 2 | 0 | 1 | 1 | 1 |
| 3 | 0 | 1 | 2 | 2 |
| 4 | 0 | 0 | 1 | 1 |
| 5 | 0 | 0 |(1)|(1)+(1)| - this means that we can target 5 with 1 way
| 6 | 0 | 0 | 0 | 0 + 1 | from set `[1,2,3]` and 1 way to achieve 5 - 4
| 7 | 0 | 0 | 0 | 0 + 2 | with the same set `[1,2,3]
| 8 | 0 | 0 | 0 | 0 + 1 |
而且很容易得到初始值。对于第1项,我们只能针对1项。这就像是一个改变问题,但你不想要最小数量的“硬币”,你想要跟踪方式的数量。此外,重要的是要注意顺序很重要。使用动态规划如下。假设我们想展示如何从1,3,5的和中得出10,其中顺序无关紧要(即1+3+1+5与1+1+3+5相同)。制作一个索引为10(11项)的数组。你可以用一种方法得到00。所有负索引都可以用0种方式创建
00 01 02 03 04 05 06 07 08 09 10
1
然后,您可以计算出1的方法数。如果你从1中减去硬币价值1,你得到1。如果你从1中减去任何较大的硬币价值,你会得到一个负指数,这意味着零。将结果加总为1路(减1)+0路(减3)+0路(减5)。所以我们有
00 01 02 03 04 05 06 07 08 09 10
1 1
取值2。我们可以减去1分硬币得到1,其中有一种方法;3分硬币获得-1,其中有0种方式;5分硬币-3分,其中有0种方式。总共1+0+0=1种方式获得2美分
00 01 02 03 04 05 06 07 08 09 10
1 1 1
对于3美分,我们可以减去1美分,以获得2美分的所有更改方式;或者减去3美分,以获得0美分的所有更改方式;或者减去5美分,以获得-2美分的所有更改方式;方法之和为1+1+0=2
00 01 02 03 04 05 06 07 08 09 10
1 1 1 2
只需4美分,我们就可以用2+1+0=3种方式完成。但是,请注意,这三种方式包括1+1+1+1、3+1和1+3。1+3和3+1属于不同的类别,因为它们以不同的数字结尾
继续以这种方式,我们有
00 01 02 03 04 05 06 07 08 09 10
1 1 1 2 3 5 8 12 19 30 47
现在我们知道了如何构造数字,我们可以寻找更快的方法。请注意,我们必须处理前几个的负指数,这有点混乱,因此我们不妨从给定的前5个值开始。我们现在需要计算的是一个递归函数:f(n)=f(n-1)+f(n-3)+f(n-5)
,条件是f(0),…,f(4)
由上表给出
有很多方法可以计算递归函数。这就是它成为对知识深度的真正考验的地方。天真的方法是在编程语言中使用递归。第一个问题是您的数字将溢出,因为f
呈指数增长。您可以使用long
而不是int
暂时缓解这种情况。但是long
也会很快溢出,所以您必须使用biginger来获得大于(我猜)20左右的n的精确结果
其次,使用递归函数会很慢,因为计算f(n)
可能需要反复计算较小的f(x)
。要缓解该问题,请使用memonization存储较小值的f(x)
。接下来,如果您尝试从较大的n
计算到较小的值,您将遇到堆栈溢出。如果要计算特定的n
,则应该从较低的值开始计算,而不是从较高的n
值开始计算
接下来,您将体验内存不足。你可以通过在x
增加时不记住f(x)
的每一个值,而只记住最后的5
值(在我的示例中)来解决这个问题。最终你会遇到存储大整数的内存不足的问题,但除非问题发生变化,否则你对此无能为力
如果你想提高程序的速度,可以使用更多的加速f(n)
可以在logn
操作中计算,而不是按以下方式在n
操作中计算。注意,有一种写递归的矩阵乘法方法,在每个阶段,我们需要记住5个值来计算下一个值:
|1 0 1 0 1| |f(n-1)| | f(n) |
|1 0 0 0 0| |f(n-2)| |f(n-1)|
|0 1 0 0 0| |f(n-3)| = |f(n-2)|
|0 0 1 0 0| |f(n-4)| |f(n-3)|
|0 0 0 1 0| |f(n-5)| |f(n-4)|
所以我们可以计算f(n)如果我们可以计算矩阵的幂:
|1 0 1 0 1|^(n-4)
|1 0 0 0 0|
|0 1 0 0 0|
|0 0 1 0 0|
|0 0 0 1 0|
我们可以通过使用n
的二进制扩展来加速这一过程。假设n=6
,就像我们想要f(10)
一样。请注意,二进制格式的6=4+2
。用M
表示矩阵。我们有M^1
。我们计算M^2=M^1*M^1
和M^4=M^2*M^2
,然后M^6=M^2*M^4
可能还有其他的加速,依情况而定