368-最大整除子集

题目

https://leetcode-cn.com/problems/largest-divisible-subset/

思路

一个问题:如何判断整数 r r r是否能够加入一个整除集 Q Q Q形成一个更大的整除集?

如果 m i n ( Q ) < r < m a x ( Q ) min(Q) < r < max(Q) min(Q)<r<max(Q),我们则需要遍历整除集 Q Q Q来判断 r r r是否能够加入该整除集,

如果 r r r需要满足以下两个条件则能直接确定能够加入 Q Q Q形成一个更大的整除集

  1. r < m i n ( Q ) ∧ r ∣ m i n ( Q ) r < min(Q) \wedge r | min(Q) r<min(Q)rmin(Q)
  2. r > m a x ( Q ) ∧ m a x ( Q ) ∣ r r > max(Q) \wedge max(Q) | r r>max(Q)max(Q)r

基于这个结论, 如果我们按大小顺序处理数组中的数字(将数组排序),则能够更好的解决下一个数字是否能够扩展已有的整除集(已有整除集按照大小顺序存储)的问题,即如果从小到大处理,当我们处理数字r时可以有以下结论
∀ 已 处 理 数 字 组 成 的 整 除 集 Q , r > m i n ( Q ) \forall 已处理数字组成的整除集Q,r>min(Q) Q,r>min(Q)
当完成array[0:p-1]的处理后,我们假设已经采用某种方式保存了array[0:p-1]中的数字组成的整除集集合 T = { Q i ∣ 0 ≤ i ≤ p − 1 } T = \{Q_i | 0 \leq i \leq p-1\} T={Qi0ip1},其中 Q i Q_i Qi 表示包含array[i]array[i]为最大值的最大整除集。

对于数组P{1,2,3,4,5,6}, Q 5 Q_5 Q5就表示包含6,且并且元素均 ≤ 6 \leq 6 6的最长的整除集,即{1,2,6}或者{1,3,6}。

我们为什么要保存包含array[i]array[i]为最大值的最大整除集 Q i Q_i Qi而不是已经处理的数字组成的所有的整除集呢?首先我们仅需保存最大的整除集 Q i Q_i Qi,因为我们需要求的是最大的整除集;另外我们对每一个 Q i Q_i Qi限制其包含array[i],因为其他不包含array[i]的一些整除集其由其他 Q Q Q保存,在所有 Q Q Q中必然存在一个全局最大的整除集

对于下一个数字p我们遍历已有的所有整除集(遍历集合 T T T),找到最大的一个可以将p加入的整除集( m a x ( Q ) ∣ r max(Q) | r max(Q)r 并且 Q Q Q中元素最多),并将新的整除集保存到整除集集合中,当所有数字处理完成后,我们找到整除集中最大的一个就是最终的结果。

如果存在多个整除集长度相同并且都可以被p加入是否,那么在其中任选一个是否会影响最终结果呢? 答案是不会

考虑

q1 = {1,2}
q2 = {1,3}
p = 6

我们可以选择将p加入q1或者将p加入q2,得到q3,此时q1和q2并没有被删除,后续的所有数字仍然可以选择加入q1或者q2,因此不存在因为p加入q1或者q2从而阻止其他数字加入q1和q2的问题。另外,后续数字能够加入集合q仅仅取决于q中的最大值,因此,q3的最大值p决定了后续什么数字可以加入q3,与q3中其他数字无关,因此不会应用最终从q3扩展得到的集合q的元素总数量

为了保存上述整除集集合 T T T,我们使用了两个数组dpdp2,其中dp[i]保存集合 Q i Q_i Qi中下一个元素的索引值,即通过dp[i]可以找到 Q i Q_i Qi中所有元素,dp2[i]则保存 Q i Q_i Qi中元素的个数。

array = {1,2,3,4,5,6}
Q_5 = {1,2,6}
dp[5] = 1
dp[1] = 0
dp[0] = -1

当处理完所有数字后,遍历dp2找到最大的 Q i Q_i Qi,然后通过dp获取 Q i Q_i Qi中所有元素的值。

代码

class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<int> dp(nums.size());
        vector<int> dp2(nums.size());
        vector<int> result;

        // 记录当前最大整除子集中上一个元素的index
        dp[0] = -1;
        // 记录当前最大整除子集的长度(dp2[p]表示最大元素为p时最大整除子集的大小)
        dp2[0] = 1;
        for(int i=1; i<nums.size(); i++) {
            dp[i] = -1;
            dp2[i] = 1;
            // 搜索当前元素可以加入的最大整除子集
            for (int j= i -1; j> -1; j--) {
                // 第二个条件判断表示加入的最大整除子集满足长度最大的要求
                if (nums[i] % nums[j] == 0 && (dp2[j] + 1) > dp2[i]) {
                    dp[i] = j;
                    dp2[i] = dp2[j] + 1;
                }
            }
        }

        int arg_max = 0;
        for(int i = 0; i < nums.size(); i++) {
            if (dp2[arg_max] < dp2[i]) {
                arg_max = i;
            }
        }

        for(int i = arg_max; i >= 0;) {
            result.push_back(nums[i]);
            i = dp[i];
        }
        return result;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值