一、题目
给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
- answer[i] % answer[j] == 0 ,或
- answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。
二、思路
提示:该题题意很容易被曲解,认为一定从第一个元素开始,或认为一定从最后一个元素结束,并且给出的两个可能性条件,也让人摸不着头脑。
1、拿到本题,首先想到的应该是排序,如果给出的nums是有序的,寻找有效子集,只需要往一个方向寻找即可。
2、先思考暴力法:暴力法就是把所有情况都遍历一下,大概推导一下,会发现有重复子问题。
如上图,可以观察到,暴力解法会重复计算16->4->1的可能性,那就将其记录下来即可,状态转移方程为:
dp[i]记录了从nums[0]到nums[i]中,以包含nums[i]的最大整除子集长度。(这里有点绕,需好好理解)
三、代码
class Solution {
public List<Integer> largestDivisibleSubset(int[] nums) {
int[] dp = new int[nums.length];
int[] route = new int[nums.length];
int maxVal = 0;
int loc = 0;
Arrays.sort(nums);
for (int i = 0; i < route.length; i++) {
route[i] = -1; // 初始化为-1,是为了防止与下标0重合
}
for (int i = 0; i < nums.length; i++) {
dp[i] = 1; // 最小会有一个元素,那就是长度为1
for (int j = 0; nums[j] * 2L <= nums[i]; j++) { //最多到num[i]/2,2L是防止乘法溢出
// 判断是否能被整除
if ((nums[i] % nums[j] == 0) && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
route[i] = j; // 记录上一个元素的下标
}
}
if (dp[i] > maxVal) { // 记录最大整除子集最后一个元素的位置
maxVal = dp[i];
loc = i;
}
}
List<Integer> ans = new ArrayList();
while (loc != -1) { //根据route记录的路线去添加元素,从后往前
ans.add(nums[loc]);
loc = route[loc];
}
Collections.reverse(ans);
return ans;
}
}
时间复杂度为O(n^2),空间复杂度为O(n)