详情请见 leetcode 最大整除子集
我的解法:
- 遍历返回(审题不严,未返回最大的整除子集)
分析题目可以看出,对数组进行从小到大排序,对于数组中的任意两个元素:
假设 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;
}
}
- 子集的性质(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,因为本身也可以构成整除子集
| nums | 2 | 4 | 7 | 8 | 9 | 12 | 16 | 20 |
|---|---|---|---|---|---|---|---|---|
| dp | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
然后从nums[ 0 ]开始遍历,得到dp[ i ];
dp[ i ] = max{ dp[ j ]+1 , dp[ i ] } 其中 j 表示能够整除的nums[ j ]的 dp
| nums | 2 | 4 | 7 | 8 | 9 | 12 | 16 | 20 |
|---|---|---|---|---|---|---|---|---|
| dp | 1 | 2 | 1 | 3 | 1 | 3 | 4 | 3 |
上述构造是按照数学性质第二条构造的,当然也可以按照第一条来构造!
| nums | 2 | 4 | 7 | 8 | 9 | 12 | 16 | 20 |
|---|---|---|---|---|---|---|---|---|
| dp | 4 | 3 | 1 | 2 | 1 | 1 | 1 | 1 |
无论按照那种方式进行动态转移规划,我们都可以得到一个解。那么如何找到这个解?
我们可以用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;
}
}
本文介绍了一种解决LeetCode上的最大整除子集问题的方法,通过动态规划来找到数组中能构成整除关系的最大子集。文章详细阐述了动态规划的思路,包括如何构建状态转移方程,并提供了具体的代码实现。

918

被折叠的 条评论
为什么被折叠?



