LeetCode每日一练——368. 最大整除子集(动规)
题目描述
给你一个由 无重复 正整数组成的集合 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 * 109
nums 中的所有整数 互不相同
解题思路
我一开始感觉这题跟最长上升子序列差不多,也是每次考虑以当前第i个数作为结尾时,都要往回看,即回溯到最前面,更新当前的状态,而且这里还需要的就是让当前第i个数跟前面每个数所在的子集都比较一遍,这样才能选出最大的那个,因为我用的是动规来做,f[i]表示的就是以第i个数结尾的整除子集,当算完所有结尾的数的子集后,最后的答案就是从这些结尾的整除子集中选出最大的那个子集来,由于题目已经明确说明了,如果存在多个有效子集,返回其中任何一个均可。这题刚开始以为不排序就可以,可是当前测试我前面的思路时,答案总是错误,最后还是给数组排了序,没想到还真出来了,虽然还是没弄清为啥排序之后就能保证正确性,希望有大佬指点一二,小弟不胜感激!!!
后来醒悟
原来没有排序的话,会有一种情况得出的答案是错误的,例如:输入[3, 27, 9, 18, 36] 如果按照我一开始没有排序的代码,那么每个数对应的子集应该是{3}、{3, 27}、{3, 27, 9}、{3, 18}、{3, 18, 36},最后返回的最大整除子集就会是{3,18,36},而实际上应该是{3, 9, 1, 36}长度为4,而不是长度为3的子集{3,18,36},这一点是真的难想到,经过这一题的教训,以后做题要多考虑边界情况,不然连自己都找不出代码到底错在哪,这题其实是跟数学相关,对数学中数字比较敏感的话,这个反例应该很容易就会想得到,所以说算法是程序的灵魂,数学是算法的基础,这句话是有一定道理的(哎,都怪以前数学没有认真学,对数字的敏感度也不是很强)。
AC代码
1 class Solution { 2 public List<Integer> largestDivisibleSubset(int[] nums) { 3 int n = nums.length; 4 if (n == 1) { 5 List<Integer> list = new ArrayList<>(); 6 list.add(nums[0]); 7 return list; 8 } 9 10 // 排序,这一点很重要 11 Arrays.sort(nums); 12 13 // 存储以i结尾的最大整除子集中的每个元素 14 List<Integer>[] list = new ArrayList[n + 1]; 15 for (int i = 1; i <= n; i++) { 16 list[i] = new ArrayList<>(); 17 } 18 19 int[] f = new int[n + 1]; 20 f[0] = 0; 21 f[1] = 1; 22 23 list[1].add(nums[0]); 24 25 for (int i = 2; i <= n; i++) { 26 f[i] = 1; 27 list[i].add(nums[i - 1]); 28 int index = i; 29 for (int j = 1; j < i; j++) { 30 boolean flag = true; 31 for (int k = 0; k < list[j].size(); k++) { 32 if (nums[i - 1] % list[j].get(k) != 0 && list[j].get(k) % nums[i - 1] != 0) { 33 flag = false; 34 break; 35 } 36 } 37 if (flag && f[j] + 1 > f[i]) { 38 // 更新 39 f[i] = f[j] + 1; 40 index = j; 41 } 42 } 43 // 这里需要做个判断,不然就会加入重复的数到集合中去 44 if (index != i) { 45 list[i].addAll(list[index]); 46 } 47 48 } 49 50 int res = 1; 51 int index = 1; 52 for (int i = 1; i <= n; i++) { 53 if (list[i].size() > res) { 54 res = list[i].size(); 55 index = i; 56 } 57 } 58 59 return list[index]; 60 } 61 }
力扣链接:https://leetcode-cn.com/problems/largest-divisible-subset/comments/