最大整除子集

本文介绍了一种解决LeetCode上的最大整除子集问题的方法,通过动态规划来找到数组中能构成整除关系的最大子集。文章详细阐述了动态规划的思路,包括如何构建状态转移方程,并提供了具体的代码实现。

最大整除子集

详情请见 leetcode 最大整除子集

我的解法:

  1. 遍历返回审题不严,未返回最大的整除子集

分析题目可以看出,对数组进行从小到大排序,对于数组中的任意两个元素:

假设 i < j ,那么只需要 nums [ i ] | nums [ j ] 即可,就为一个整除子集。

如果不含有这种情况,那么整出子集必定只含有一个元素,故任意即可。

class Solution {
    public List<Integer> largestDivisibleSubset(int[] nums) {
        Arrays.sort(nums);
        List<Integer> res = new ArrayList<Integer>();
        for(int i=0;i<nums.length-1;i++) {//进行遍历
            for(int j=1;j<nums.length;j++) {
                if(nums[j]%nums[i]==0) {//存在整除子集 返回结果
                    res.add(nums[i]);
                    res.add(nums[j]);
                    return res;
                }
            }
        }
        res.add(nums[i]);//如果不含有模大于1的整除子集
        return res;
    }
}
  1. 子集的性质(dfs,但是超时时间限制)

我们可以寻找出nums的所有子集,然后从子集中筛选出整除子集,并且返回其中长度最大的子集。

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;

    void dfs(int cur, vector<int>& nums) {
        if (cur == nums.size()) {
            ans.push_back(t);
            return;
        }
        t.push_back(nums[cur]);
        dfs(cur + 1, nums);
        t.pop_back();
        dfs(cur + 1, nums);
    }

    int isAnswer(vector<int>& nums) {
        for(int i=0;i<nums.size()-1;i++) {
            for(int j=i+1;j<nums.size();j++) {
                if(nums[j]%nums[i]!=0) return 0;
            }
        }
        return 1;
    }

    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        dfs(0,nums);//得到所有子集
        int flag=0;//整除子集最大长度
        int position=0;//最大长度子集对应的位置
        for(int i=0;i<ans.size();i++) {
            if(isAnswer(ans[i])==1) {
                if(ans[i].size()>flag) {//比较
                    position=i;
                    flag=ans[i].size();
                }
            }
        }
        return ans[position];//返回满足题意的最大整除子集
    }
};

动态规划:

首先明确一个数学性质:

如果一个集合为整除子集,其中最大值为maxValue,最小值为minValue,那么如何扩充这个子集?

  • 如果 a 可以整除minValue,那么 a 一定可以添加到整除子集中;

  • 如果maxValue可以整除 b,那么 b 一定可以添加到整除子集中。

由上述性质,就可以得到一个动态转移关系。

不妨设dp数组,dp[ i ]存储以nums[ i ]为最大整数的整除子集的大小

因此在最开始,所有的dp[ i ]都为1,因为本身也可以构成整除子集

nums24789121620
dp11111111

然后从nums[ 0 ]开始遍历,得到dp[ i ];

dp[ i ] = max{ dp[ j ]+1 , dp[ i ] } 其中 j 表示能够整除的nums[ j ]的 dp

nums24789121620
dp12131343

上述构造是按照数学性质第二条构造的,当然也可以按照第一条来构造!

nums24789121620
dp43121111

无论按照那种方式进行动态转移规划,我们都可以得到一个解。那么如何找到这个解?

我们可以用maxSize来记录整个动态转移规划过程中的整除子集的最大数目,然后maxSize-1向前搜寻满足题意的解

与此同时也用maxValue来记录maxSize对应的数据元素,以便返回解

按照第二条性质动态转移的代码如下:

class Solution {
    public List<Integer> largestDivisibleSubset(int[] nums) {
        int len = nums.length;
        Arrays.sort(nums);
        // 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数
        int[] dp = new int[len];
        Arrays.fill(dp, 1);
        int maxSize = 1;
        int maxVal = dp[0];
        for (int i = 1; i < len; i++) {
            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] > maxSize) {
                maxSize = dp[i];
                maxVal = nums[i];
            }
        }
        // 第 2 步:倒推获得最大子集
        List<Integer> res = new ArrayList<Integer>();
        if (maxSize == 1) {
            res.add(nums[0]);
            return res;
        }        
        for (int i = len - 1; i >= 0 && maxSize > 0; i--) {
            if (dp[i] == maxSize && maxVal % nums[i] == 0) {
                res.add(nums[i]);
                maxVal = nums[i];
                maxSize--;
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值