368. 最大整除子集-每日一题

一、题目

        给你一个由 无重复 正整数组成的集合 nums ,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:

  • answer[i] % answer[j] == 0 ,或
  • answer[j] % answer[i] == 0

        如果存在多个有效解子集,返回其中任何一个均可。

点击查看原题

二、思路

        提示:该题题意很容易被曲解,认为一定从第一个元素开始,或认为一定从最后一个元素结束,并且给出的两个可能性条件,也让人摸不着头脑。
        1、拿到本题,首先想到的应该是排序,如果给出的nums是有序的,寻找有效子集,只需要往一个方向寻找即可。
        2、先思考暴力法:暴力法就是把所有情况都遍历一下,大概推导一下,会发现有重复子问题。
image.png
        如上图,可以观察到,暴力解法会重复计算16->4->1的可能性,那就将其记录下来即可,状态转移方程为:
d p [ i ] = { m a x ( d p [ i ] , d p [ j ] + 1 ) , n u m s [ i ] % n u m s [ j ] = = 0 1 , e l s e dp[i]= \left\{ \begin{aligned} max(dp[i],dp[j]+1)&,& nums[i] \% nums[j] == 0 \\ 1 &,&else \\ \end{aligned} \right. dp[i]={max(dp[i],dp[j]+1)1,,nums[i]%nums[j]==0else
        dp[i]记录了从nums[0]到nums[i]中,以包含nums[i]的最大整除子集长度。(这里有点绕,需好好理解)

        再用一个route来记录最大整除子集的元素下标路线。

三、代码

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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

佳鑫大大

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值