1. 问题描述:
给你一个由无重复正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0 ,或
answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。
示例 1:
输入:nums = [1,2,3]
输出:[1,2]
解释:[1,3] 也会被视为正确答案。
示例 2:
输入:nums = [1,2,4,8]
输出:[1,2,4,8]
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 2 * 10 ^ 9
nums 中的所有整数互不相同
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-divisible-subset/
2. 思路分析:
首先一看到题目感觉没有思路,最好的办法是画图。我们可以根据题目的描述画一下具体的图示发现是否存在某种规律。首先对nums由小到大排序,排序并不影响求解子集的问题。排序之后可以发现如果长度为k的子序列为最大的子集那么一定满足子序列中后一个数字是前一个数字的倍数,这样对于任意的ai与aj他们都是互为倍数关系,所以问题就转化为了求解最长上升子序列的问题,我们可以使用最长上升子序列的思路求解出子序列最长的长度。因为需要求解出构成子序列的方案,其实由dp值求解最佳方案可以使用倒着推导的思路,我们在求解最长上升子序列的时候维护一个最长子序列中最后一个数字的下标,通过这个下标逆推最优方案。首先dp[k]对应的nums[k]一定是子集中的最后一个数字,所以我们倒着递推即可,找到更新当前dp[k]的上一个下标,直到当前的dp[k] = 1说明最长子序列的方案更新结束。
3. 代码如下:
from typing import List
class Solution:
def largestDivisibleSubset(self, nums: List[int]) -> List[int]:
n = len(nums)
nums.sort()
dp = [0] * n
k = 0
for i in range(len(nums)):
dp[i] = 1
for j in range(i):
if nums[i] % nums[j] == 0:
dp[i] = max(dp[i], dp[j] + 1)
# 求解当前当前最长子序列的最后一个位置
if dp[k] < dp[i]:
k = i
# 找出构成最长子序列的方案
res = [nums[k]]
# 只有当子序列的长度大于1的时候才执行循环
while dp[k] > 1:
# 倒推答案, 找出上一个更新当前的dp[k]的下标
for i in range(k):
if nums[k] % nums[i] == 0 and dp[k] == dp[i] + 1:
k = i
res.insert(0, nums[i])
break
return res