题目
难度:★★★★☆
类型:数组
方法:数学,深度优先搜索
给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (Si,Sj) 都要满足:Si % Sj = 0 或 Sj % Si = 0。
如果有多个目标子集,返回其中任何一个均可。
示例
示例 1:
输入: [1,2,3]
输出: [1,2] (当然, [1,3] 也正确)
示例 2:
输入: [1,2,4,8]
输出: [1,2,4,8]
解答
对于组合类问题,我们可以先把数字排序,然后使用动态规划方式来计算。
动态规划的dp矩阵定义,这里我们使用字典来定义,字典的键为数组nums(排序后)的下标i,字典的值为到数字nums[i]为止可以构成的最大整数子集,为了便于描述,我们将到达数字nums[i]为止可以构成的最大整数子集定义为函数f(i),在代码实现上,f(i)=dp[i],是一个最大整数子集的列表。
初始状态:由于f(i)中至少可以包含一个数字,就是i所对应的nums数组中的数字本身nums[i],因此可以将f(i)初始化为nums[i]。
状态转移方程。我们需要对nums中每一个数字所对应的f(i),(0<=i<=len(nums)-1)进行计算,但是计算f(i)是需要用到之前已经计算得到的结果的,这就是动态规划在这里的作用。获得f(i)可以通过从大到小遍历的方式(j从i-1到0),逐个查看dp[i]能否被nums[j]整除,如果可以,就可以考虑将当前数字nums[i]加入到dp[j]中组成新的dp[i],如果比就当前的dp[i]更长,那么更新当前dp[i],这里通过max函数获取最长列表,并且通过其中的参数key来设置排序的方式,即按照长度排序并取最长的元素。
返回结果。递归完成后,dp中就保存有所有数字的最长整数子集,然后再从中选取一个最长的就好了。
class Solution(object):
def largestDivisibleSubset(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
if len(nums) <= 1:
return nums
nums = sorted(nums)
dp = {i: [nums[i]] for i in range(len(nums))} # 至少子集可以做一个子集
for i in range(1, len(nums)): # 求取每一个数字所对应的最大整数子集
for j in range(i - 1, -1, -1): # 研究当前数字能够被小于这个数字的所有数整除
if nums[i] % nums[j] == 0: # 如果遇到j可以被i整除,那么把j所对应的最大子集包含在i的位置。
list_up_to_i = dp[j] + [nums[i]] # 到j为止的最大子集
dp[i] = max(list_up_to_i, dp[i], key=len) # 由于可能有多个数可以被i整数,在子集中选最长的保存,每次都要更新
return max(dp.values(), key=len) # 选出来所有值所对应的最长子集中最长的
如有疑问或建议,欢迎评论区留言~