[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, or
  • answer[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 }

 

LIS类相关题目

LeetCode 题目总结

posted @ 2020-06-14 10:54  CNoodle  阅读(226)  评论(0编辑  收藏  举报