[LeetCode] 368. Largest Divisible Subset
Given a set of distinct positive integers nums
, return the largest subset answer
such that every pair (answer[i], answer[j])
of elements in this subset satisfies:
answer[i] % answer[j] == 0
, oranswer[j] % answer[i] == 0
If there are multiple solutions, return any of them.
Example 1:
Input: nums = [1,2,3] Output: [1,2] Explanation: [1,3] is also accepted.
Example 2:
Input: nums = [1,2,4,8] Output: [1,2,4,8]
Constraints:
1 <= nums.length <= 1000
1 <= nums[i] <= 2 * 109
- All the integers in
nums
are unique.
最大整除子集。
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0 ,或
answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/largest-divisible-subset
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路是动态规划。这个题的思路跟300题很像,但是这个题需要排序。首先,两个不同的数字 A 和 B,既然不同,那么一定一大一小,一定意味着小的除以大的的余数一定不为 0。题目既然规定了 A%B = 0 或者 B%A = 0 都可以满足题意,我们可以先将 input 数组从小到大排序。这样扫描数组的时候可以从当前位置往左看,看当前数字 nums[i] 是否可以整除比他小的数字。
接着创建一个数组 count[i],记录的是以 nums[i] 结尾的,最大的满足题意的子集大小。这个数组 count[i] 初始化的时候每个位置都是 1,因为每个数字除以他本身的余数都为 0。接着会用到两个 for loop,一个是遍历nums数组,一个是将 nums[i] 去跟比他自己小的所有的数字都去做一下除法,看看有多少数字能跟 nums[i] 相除 == 0。这部分的逻辑跟 300 题是一样的。
接着再次扫描 count[i] 数组,找出最大的 maxIndex,maxIndex 变量表示的是集合的最大长度的下标在什么地方。count[maxIndex] 是最长的子串的长度,同时找到的最大值背后的 nums[maxIndex] ,也是这个子串的最后一个元素。
最后一步是从 nums[maxIndex] 开始往前扫描,如果遇到任何一个数字 nums[i] 能被 nums[maxIndex] 整除,同时 count[i] 也对的上号,则把这个数字加入结果集。什么意思呢?比如我们给一个例子 [4, 8, 10, 240]。我们能找到最大值是 240,而且 maxIndex 会从 240 的下标 3 开始往前走,如果什么都不做,就会直接判断下一个数字 10。10 就比较tricky了,他可以被 240 整除但是他其实是不在最后的结果集里面的。我们是怎么知道他不在结果集里的呢?去看 count[i]。对于一个更小的数字,我们不但要看他是否能被 nums[maxIndex] 整除,同时也要看他的 count[i] 值是否正确,因为如果这个数字属于最后的结果集,他的 count[i] 值应该对应的是他在最后结果集里的 index。因为 count[i] 的意思是以当前这个数字结尾的,符合题意的子序列的长度。
所以我们必须额外去记录每当一个数字符合条件被加入结果集之后,size 要--,这样只有跟 count[i] 能对应的上的 size 背后的数字才是这一个子集里面的数字。
时间O(n^2)
sort - O(nlogn)
扫描,写入count[i]的值 - O(n^2)
空间O(n)
Java实现
1 class Solution { 2 public List<Integer> largestDivisibleSubset(int[] nums) { 3 List<Integer> res = new ArrayList<>(); 4 int n = nums.length; 5 // corner case 6 if (n == 0) { 7 return res; 8 } 9 10 // normal case 11 Arrays.sort(nums); 12 // 以nums[i]结尾的,最大的满足题意的子集大小 13 int[] dp = new int[n]; 14 Arrays.fill(dp, 1); 15 for (int i = 1; i < n; i++) { 16 for (int j = i - 1; j >= 0; j--) { 17 if (nums[i] % nums[j] == 0) { 18 dp[i] = Math.max(dp[i], dp[j] + 1); 19 } 20 } 21 } 22 23 // 最长子序列的最后一个元素的index 24 int maxIndex = 0; 25 for (int i = 1; i < n; i++) { 26 maxIndex = dp[i] > dp[maxIndex] ? i : maxIndex; 27 } 28 29 int max = nums[maxIndex]; 30 int size = dp[maxIndex]; 31 for (int i = maxIndex; i >= 0; i--) { 32 if (max % nums[i] == 0 && dp[i] == size) { 33 res.add(nums[i]); 34 max = nums[i]; 35 size--; 36 } 37 } 38 return res; 39 } 40 }