leetcode 368最大整除子集

博客围绕最大整除子集问题展开,先对问题进行基本分析,提出对集合排序后取数的思路。接着运用动态规划,指出这是序列DP问题,类似最长上升子序列变形。定义状态和状态转移方程,还使用额外数组记录状态转移。最后可遍历数组取得结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Question 3: Largest Divisible Subset

Given a set of distinct positive integers, find the largest subset such that every pair (𝑆𝑖, 𝑆𝑗) of elements in this subset satisfies: 𝑆𝑖%𝑆𝑗 = 0 or 𝑆𝑗%𝑆𝑖 = 0. Please return the largest size of the subset. Note: 𝑆𝑖%𝑆𝑗 = 0 means that 𝑆𝑖 is divisible by 𝑆𝑗.

1、代码如下


class Solution {
    public List<Integer> largestDivisibleSubset(int[] nums) {

        Arrays.sort(nums);
        List<Integer>list=new ArrayList<>();
        int n=nums.length;
        int[]dp=new int[nums.length];//以nums[i]结尾的最大整除子集个数
        int[]s=new int[n];//s数组记录最优子结构
        int maxsize=1,k=0;//maxsize记录最大整除子集个数,k用来记录最大dp下标
        Arrays.fill(dp,1);//子集最小为本身元素
        for(int i=0;i<n;i++){
            int pre=i;//pre记录s的前一个元素构成
            for(int j=0;j<i;j++){
                if(nums[i]%nums[j]==0) {
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                    if(dp[i]==dp[j]+1){
                        pre=j;
                    }
                }
            }
            s[i]=pre;
            //找最大整除子结构个数和下标
            if(dp[i]>maxsize){
                k=i;
                maxsize=dp[i];
            }
        }
        //回溯找最优子结构
        while(list.size()!=maxsize){
            list.add(nums[k]);
            k=s[k];
        }
        return list;
    }
}

2、基本分析

根据题意:对于符合要求的「整除子集」中的任意两个值,必然满足「较大数」是「较小数」的倍数。

由于存在「整除子集」中任意两个值必然存在倍数/约数关系的性质,我们自然会想到对 nums 进行排序,然后从集合 nums 中从大到小进行取数,每次取数只考虑当前决策的数是否与「整除子集」中的最后一个数成倍数关系即可。

这时候你可能会想枚举每个数作为「整除子集」的起点,然后从前往后遍历一遍,每次都将符合「与当前子集最后一个元素成倍数」关系的数加入答案。

3、动态规划

基于上述分析,我们不难发现这其实是一个序列 DP 问题:某个状态的转移依赖于与前一个状态的关系。即 nums[i] 能否接在 nums[j] 后面,取决于是否满足 nums[i] % nums[j] == 0 条件。

可看做是「最长上升子序列」问题的变形题。

定义 dp[i]为考虑前 i 个数字,且以第 i 个数为结尾的最长「整除子集」长度。

我们不失一般性的考虑任意位置 i,存在两种情况:

如果在 i 之前找不到符合条件 nums[i] % nums[j] == 0 的位置 j,那么 nums[i] 不能接在位置 i 之前的任何数的后面,只能自己独立作为「整除子集」的第一个数,此时状态转移方程为 dp[i]=1;
如果在 i 之前能够找到符合条件的位置 j,则取所有符合条件的 dp[j] 的最大值,代表如果希望找到以 nums[i] 为结尾的最长「整除子集」,需要将 nums[i] 接到符合条件的最长的 nums[j] 后面,此时状态转移方程为 dp[i]=dp[j]+1。
同时由于我们需要输出具体方案,需要额外使用 s[] 数组来记录每个状态是由哪个状态转移而来。

定义 s[i] 为记录 dp[i] 是由哪个下标的状态转移而来,如果 dp[i]=dp[j]+1, 则有 s[i]=j。

对于求方案数的题目,多开一个数组来记录状态从何转移而来是最常见的手段。

当我们求得所有的状态值之后,可以对 dp[] 数组进行遍历,取得具体的最长「整除子集」长度和对应下标,然后使用 s[] 数组进行回溯,取得答案。

4、证明正确性

在这里插入图片描述

5、复杂度分析

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值