一、题目
给你一个由 无重复 正整数组成的集合 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; // 最小会有一个元素,那就是长度为1for (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)
