有一个无序,元素个数为2n的正整数数组,要求:如何能够把这个数组分割为元素个数为n的两个数组,并使得两个数组的和最接近?
分析和解法题目的本质就是要用2n个整数中找出n个,使得它们的和尽可能地靠近所有整数地一半。
解法 1一个最为直接地暴力解法是:
- 先将数组的所有元素进行排序
- 将它们划分成为两个子数组
- 从和中找出一对数进行交换,使得和直接的插值尽可能的小,直到找不到可对换的为止。
该算法的缺点是和的值不一定是最优的,优点是 和 元素的个数相差最小。
解法 2假设2n个整数的和是SUM。那么从2n个整数中找到n个元素的和,有三种可能的情况:大于SUM/2,等于SUM/2,小于SUM/2。因为是二分,因此我们只需要考虑小于等于SUM/2的情况就可以了。
可以使用动态规划的方法解决这个问题:
- 可以将问题分为2n步,第k步的定义是前k个元素是前k个元素中任意i个元素的和,所有可能的取值集合为.(这里只考虑小于等于SUM/2的情况)
- 然后将第k步拆成两个小步。即首先得到前k-1个元素。通过k-1个元素的集合中所有的元素加nums[k]得到集合为。
- 通过集合中的元素可逆推出子数组中的元素。
改时间复杂度为O(2^N), 因为heap[i+1].append(heap[i][j]+nums[k-1])代码中需要实现的次数至多是2^(n-1)次。
import heapq
nums = [1,5,7,8,9,6,3,11,20,17]
heap = [[0]]+[[] for _ in range(len(nums)//2)]
# heap[i]表示从nums中取i个数所能产生的和的集合
n = len(nums)//2
for k in range(1,2*n+1):
i_max = min(k-1,n-1)
for i in range(i_max,-1,-1):
for j in range(len(heap[i])):
if heap[i][j]+nums[k-1]<=sum(nums)//2:
heap[i+1].append(heap[i][j]+nums[k-1])
print(sum(nums)//2)
for i in range(len(heap)):
heapq.heapify(heap[i])
print(heap[i])
解法 3
根据解法2的方法,可以设计类似于背包子集问题的解法。背包子集问题也是动态规划的一种。
nums = [1,5,7,8,9,6,3,11,20,17]
n, sum = len(nums), sum(nums)
isOK = [[False]*(sum//2+1) for _ in range(n//2+1)]
isOK[0][0]=True
for k in range(1,n+1):
for i in range(min(k,n//2),0,-1):
for v in range(1,sum//2+1):
if v >= nums[k-1] and isOK[i-1][v-nums[k-1]]:
isOK[i][v] = True
for i in range(n//2+1):
for j in range(sum//2+1):
print(isOK[i][j],end=' ')
该算法的时间复杂度为O(N*N*sum)。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)