给出一个由无重复的正整数组成的集合,找出其中最大的整除子集,子集中任意一对 (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]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/largest-divisible-subset
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路:
如果只是求解最大整数子集的长度,那么这就是一道最简单的动态规划题目。但是这题目要求将最大整数子集的列表序列返回,因此就要一个动态数组path记录随dp数组变化的最终整数子集的索引(形式:path[索引] = 索引),或者使用List<List<Integeer>> ans这种链表中包含链表的形式随时记录更新的最大整数子集的集合。
数论:
给定升序序列(即 E < F < G)[E, F, G],并且该列表本身满足问题中描述的整除子集,就不必枚举该子集的所有数字,在以下两种情况:
推论一:可除以整除子集中的最大元素的任何值,加入到子集中,可以形成另一个整除子集,即对于所有 h,若有 h % G == 0,则 [E, F, G, h] 形成新的可除子集。
推论二:可除以整除子集中最小元素的任何值,加入到子集中,可以形成另一个整除子集,即,对于所有的 d,若有 E % d == 0,则 [d, E, F, G] 形成一个新的整除子集。
由此事先对nums数组进行排序操作,之后按照动态规划的思想对问题进行分析。
结合数学的理论,如果当前的元素能够整除前面集合的最大的元素,那么这个元素就可以整除前面集合中任何一个元素,也就是说,当前元素可以作为前面最大整除子集的一个元素,故更新最大整除子集,更新子集序列,更新记忆化数组dp。
dp[i]:表示当前位置i之前的所有元素中,能够成最大整除子集的个数。
按照上面的递推关系,依次让每一个元素作为最后一个元素,遍历前面元素,更新dp数组和动态数组或链表中的链表序列。
其实看到有的人c++代码中使用的是vector容器保存每个元素前面的整除集合。vector相当于是一个二维数组,只不过二维数组中的列不设定约束,同理链表中泛化的链表。
动态数组的做法,代码如下:
class Solution {
public List<Integer> largestDivisibleSubset(int[] nums) {
List<Integer> ans = new ArrayList<Integer>() ;
int len = nums.length;
if(len==0)
return ans ;
Arrays.sort(nums) ;
int[] path = new int[len] ;
int[] dp = new int[len] ;
int maxSet = 1,cur = 0;
for(int i=0; i<len; i++){
dp[i] = 1;
}
for(int x=1; x<len; x++) {
for(int y=0; y<x; y++) {
if(nums[x] % nums[y] == 0) {
if(dp[x] < dp[y] + 1) {
dp[x] = dp[y] + 1;
path[x] = y;
if(maxSet < dp[x]) {
maxSet = dp[x] ;
cur = x ;
}
}
}
}
}
for(int i = 0; i<maxSet; i++) {
ans.add(nums[cur]) ;
cur = path[cur] ;
}
return ans ;
}
}
链表泛化链表记录子集的方法,有很多要点需要注意,代码如下:
class Solution {
public List<Integer> largestDivisibleSubset(int[] nums) {
List<List<Integer>> ans = new ArrayList<List<Integer>>() ;
int len = nums.length;
if(len==0)
return new ArrayList() ; // 空数组时,需要返回一个空的List<Integer>对象,不可以直接返回ans.get(0)。因为没有new过ArrayList对象,直接操作,遇见空数组直接报越界
int index = 0,maxSet = 0;
Arrays.sort(nums) ;
ans.add(new ArrayList());
ans.get(0).add(nums[0]) ;// 上面需要在创建的泛化ans对象中,添加一个List<Integer>对象,才可以调用get函数
for(int x = 1; x<len; x++) {
ans.add(new ArrayList());
for(int y = 0; y<x; y++) {
if(nums[x]%nums[y]==0 && ans.get(y).size() > ans.get(x).size()) {
ans.get(x).removeAll(ans.get(x)) ;
ans.get(x).addAll(ans.get(y)); // 当前元素所代表的最大整数集合需要将前面的子集添加到当前元素的子集,所以需要事先清空当前元素的整除子集。
}
}
ans.get(x).add(nums[x]) ;
if(ans.get(x).size() > maxSet) {
maxSet = ans.get(x).size() ;
index = x;
}
}
return ans.get(index) ;
}
}

本文介绍了如何解决LeetCode上的最大整除子集问题,通过动态规划和数论方法找到满足条件的最大整数子集。示例包括输入输出,解题思路涉及动态数组和链表记录子集。同时,强调了对输入序列进行排序的重要性,并提供了两种不同的解决方案:一种是更新动态数组,另一种是使用链表记录子集。

3508

被折叠的 条评论
为什么被折叠?



