题目
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形成一个更大的整除集
- r < m i n ( Q ) ∧ r ∣ m i n ( Q ) r < min(Q) \wedge r | min(Q) r<min(Q)∧r∣min(Q)
- 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={Qi∣0≤i≤p−1},其中
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,我们使用了两个数组dp
和dp2
,其中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;
}
};