[C++&Rust]LeetCode No.368 最大整除子集

原贴地址:http://leanote.com/blog/post/6082bc6fab64410dd50008ec

题目

给你一个由 无重复 正整数组成的集合 nums
,请你找出并返回其中最大的整除子集 answer ,子集中每一元素对 (answer[i], answer[j]) 都应当满足:
answer[i] % answer[j] == 0 ,或
answer[j] % answer[i] == 0
如果存在多个有效解子集,返回其中任何一个均可。

来源:力扣(LeetCode)
链接:https://leetcofde-cn.com/problems/largest-divisible-subset
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路分析

这个题目有点难,一开始我想用dfs做,但是粗略分析了一下,复杂度非常高,假设数组长度是n,dfs枚举全组合的复杂度是O(2^n),时间过长。
但是dfs的思路非常好理解,就是在一个数组的后面加上一个可以被该数组最后一个数字整除的数。

class Solution {
public:
    vector<int> ret;
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> cur;
        dfs(nums, cur, -1);
        return ret;
    }
    void dfs(vector<int>& nums, vector<int>& cur, int pos) {
        if (cur.size() > ret.size()) ret = cur;
        for (int i = pos + 1; i < nums.size(); ++i) {
            if (pos == -1 || nums[i] % nums[pos] == 0) {
                cur.push_back(nums[i]);
                dfs(nums, cur, i);
                cur.pop_back();
            }
        }
    }
};

这是dfs的思路,也就是模拟的思路,当然最后是超时了。
所以我们得从复杂度开始考虑这个问题。
众所周知,dfs的最佳替代品当然就是dp啦,但是这个问题可以通过dp来解决吗,关键还在于能不能分成子问题,或者说有没有递推关系。我们可以看出,如果我们对原数组进行一个排序,因为大的数字在后面,所以对一个数来说,构造以该数结尾的最大整除子集必须在原数组中的该数前面寻找,寻找一个能被该数约分的数。
这么说可能有点迷惑,我们加一个示例来看。

nums(原数组)24789121620
dp12131343

我们假设我们不知道8的dp值,但是我们知道8之前的dp值,我们就应该在8之前寻找能够整除8的数字,其中有2和4,我们将这些数字的dp值求最大值再加1,就能推出以8结尾的最大整除子集的最大长度。同理,dp(16)=max(dp(2),dp(4),dp(8))+1等于4,要是在前方没有找到能够整除的数,最大子集的长度就是自己一个元素,就是1。
但是我们通过dp只找出来了最大的长度,并没有找出实际的数组,那么我们怎么通过长度还原出原数组呢?当然是从后向前递推,因为这个最大整除子集一定是存在于原数组中的,而且有一个特定的规律,若我们的最大整除子集是[2,4,8,16],那么这些数字在原数组中一定是按照升序排列的(因为原数组就是升序的),而且他们的dp值是1,2,3,4这样的规律,因为如果是不连续的,多出来的那个数字就可以加入到最大整除子集里面来,就自我矛盾了,所以一定是按照1,2,3,4这样的顺序,而且他们的后一个可以被前一个整除。通过这三个特点,我们在从后向前遍历的时候,按照dp值和能否被整除来寻找下一个值,最终就可以还原出最大整除子集。
C++代码

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<int> dp(n, 1);
        pair<int, int> maxpair = {dp[0], nums[0]};
        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++)
                if (nums[i] % nums[j] == 0) 
                    dp[i] = max(dp[i], dp[j] + 1);
            if (dp[i] > maxpair.first) 
                maxpair = {dp[i], nums[i]};
        }
        vector<int> res;
        if (maxpair.first == 1) {
            res.push_back(nums[0]);
            return res;
        }
        for (int i = n - 1; i >= 0 && maxpair.first > 0; i--) {
            if (dp[i] == maxpair.first && maxpair.second % nums[i] == 0) {
                res.push_back(nums[i]);
                maxpair.second = nums[i];
                maxpair.first--;
            }
        }
        return res;
    }
};

Rust代码

impl Solution {
    pub fn largest_divisible_subset(mut nums: Vec<i32>) -> Vec<i32> {
        let n = nums.len();
        nums.sort_unstable();
        let mut dp = vec![1; n];
        let mut maxpair = [dp[0], nums[0]];
        for i in 1..n {
            for j in 0..i {
                if nums[i] % nums[j] == 0 {
                    dp[i] = dp[i].max(dp[j] + 1);
                }
            }
            if dp[i] > maxpair[0] {
                maxpair = [dp[i], nums[i]];
            }
        }
        if maxpair[0] == 1 {
            return vec![nums[0]];
        }
        let mut ret = vec![];
        for i in (0..n).rev() {
            if maxpair[0] <= 0 {
                break;
            }
            if dp[i] == maxpair[0] && maxpair[1] % nums[i] == 0 {
                ret.push(nums[i]);
                maxpair[1] = nums[i];
                maxpair[0] -= 1;
            }
        }
        ret
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值