Skip to content

Latest commit

 

History

History
 
 

1982.Find-Array-Given-Subset-Sums

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

1982.Find-Array-Given-Subset-Sums

我们先考虑比较简单的情况,所有的数字都是正数。那么显然sums里面的最小值一定是0. 并且sums的第二小的值x一定是这个数组中的最小元素。有了这个x,我们就可以把sums拆解成两部分:一部分是原数组元素(假设有n个)扣除x之后的所有子集和,共有2^(n-1)个;另一部分是前者的每个元素加上x,同样也有2^(n-1)个。这样两部分的并集就是原数组所有元素的subset sums。我们利用这个方法可以不停地剥离出最小元素,继而递归处理sums砍半之后的第一部分(因为没有了x的影响)。直至把原数组的所有元素都重构出来。

那么如何做到上述的拆解呢?我们先找sums里面的最大值a,它必然是所有元素的和,属于第二部分。相对应的,第一部分里必然会有一个a-x。于是我们可以将a和a-x都从sums里删除,同时将a-x放入待递归处理的sums2. 之后不断重复之前的操作,从大到小遍历sums里剩余的值,共进行2^n/2轮。最后我们得到的sums2就是sums的一半,sum2所包含的所有子集和都已经剔除了x的影响。之后递归处理sums2即可。

OK,我们接下来考虑数字元素有正有负的情况。我们有没有机会确认其中一个元素呢?我们可以想到sums里面的最大值a,此时对应的应该是所有正数元素之和。那么sums里面的次大值b,对应的是什么呢?其实有两种可能,一种从所有正数元素之和里刨掉最小的那个,也有可能是把所有正数元素之和再加上最接近0的一个负数。不管哪种情况,我们令x=a-b,都有可能对应着原数组里的一个元素。如果是前者,那么x本身就是。如果是后者,那么-x就是。于是这里就出现了递归的分叉,我们可以将x作为一个原始元素将sums做分解,也可以将-x作为一个原始元素将sums做分解。对于分解成功的方案,我们就可以继续递归处理从sums砍半得到的sums2.

注意,当我们根据-x做sums的分解时,因为(-x)是负数,方法略有不同。此时sums里面的最小值a才是包含了(-x)的子集和,对应的a+x才是剔除了(-x)的子集和。所以我们需要从小到大遍历sums的元素。

递归的边界条件是当n=1时,sums里有两个元素。其中一个元素必须是0,另一个元素就是返回值(原始元素)。